////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
//  File name:   AudioDeviceFmodEx400.cpp
//  Version:     v1.00
//  Created:     8/6/2005 by Tomas
//  Compilers:   Visual Studio.NET
//  Description: FmodEx 4.00 Implementation of a Audio Device
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"

#ifdef SOUNDSYSTEM_USE_FMODEX400

#include "AudioDeviceFmodEx400.h"
#include <CryThread.h>
#include <ISystem.h>
#include <IConsole.h>
#include <ICryPak.h> 
#include <CrySizer.h>
#include <IRenderer.h>
#include <ITimer.h>
#include <IGame.h>
#include <IGameFramework.h>
#include <limits>
#include "SoundCVars.h"
#include "SoundBufferFmodEx400.h"
#include "PlatformSoundFmodEx400.h"
#include "SoundBufferFmodEx400Event.h"
#include "PlatformSoundFmodEx400Event.h"
#include "SoundBufferFmodEx400Micro.h"
#include "SoundBufferFmodEx400Network.h"
#include "ISound.h"
#include "Sound.h"
#include "SoundSystem.h"
#include <FmodEx/inc/fmod_errors.h>
#include <FmodEx/inc/fmod_dsp.h>
#include <FmodEx/inc/fmod.hpp>
#include <FmodEx/inc/fmod_event.hpp>
#ifdef INCLUDE_FMOD_EVENTNET_SYSTEM
 #include <FmodEx/inc/fmod_event_net.hpp>
#endif // INCLUDE_FMOD_EVENTNET_SYSTEM
#include "SoundSystemCommon.h"
#include "ISoundMoodManager.h"
#include "IReverbManager.h"
#include "IDiskProfiler.h"
#include "../CryAction/ILevelSystem.h"
#include "FileCacheManager.h"
#include "IPlatformOS.h"

#include <IJobManager_JobDelegator.h>

// Rounding macro to prevent white static noise (possible FMOD bug, needs to be compensated here).
// We round to 3 digits after the comma which keeps the listener still accurate within millimeters.
// Also converting a CryENGINE vector to an FMOD vector.
#define ROUND_AUDIO_LISTENER_VECTOR(vOut, vIn) \
  vOut.x = floor(vIn.x * 1000.0f) / 1000.0f; \
  vOut.y = floor(vIn.z * 1000.0f) / 1000.0f; \
  vOut.z = floor(vIn.y * 1000.0f) / 1000.0f; \
















void FmodJob(int a, int b, int c, int d, int *pRet) {}

DECLARE_JOB("fmod_job", TFmodJob, FmodJob)

static int FmodJobCallback(int arg1, int arg2, int arg3, int arg4)
{
































	// for PS3, the return value needs to be static, to move the variable away from the stack



	int nReturnCode;


	// forward to the job system, which will either offload the job to a secondary processor (the SPUs), or directly invoke the function in the calling context
	TFmodJob job(arg1, arg2, arg3, arg4, &nReturnCode);
	job.SetCacheMode(JobManager::SPUBackend::eCM_None); 
	job.SetJobType(JobManager::SPUBackend::eOffLoadJob);
	job.SetPriorityLevel(JobManager::eHighPriority);
	job.Run();













	return nReturnCode;
}


static int32 s_nFileOpenCount = 0; // open file handle counting through FMOD filesystem callback
static size_t s_nOpenFileID   = 0;

#if defined(USE_RSX_MEM)
	unsigned int PS3_MEM_FLAG = 0;
#endif










#define USE_POOL_ALLOCATOR

#define FSB_MAX_HEADER_CACHE_SIZE (256*1024)

//#include <windows.h> // used for get currentthread streaming
//////////////////////////////////////////////////////////////////////////
#define IS_FMODERROR (m_ExResult != FMOD_OK )
// this starts off not active
#define GETMEMORYSTATSEVERY 1000
// ****OPTIONAL**** set the size of the mixing buffer in milliSeconds 
#define FMOD_MIXING_BUFF_SIZE 200 // mixing buffer size in millaseconds ** NOT USED AT THIS TIME ******
 
CFrameProfiler* CAudioDeviceFmodEx400::s_pFrameProfiler_API_Info;
CFrameProfiler* CAudioDeviceFmodEx400::s_pFrameProfiler_API_Other;
CFrameProfiler* CAudioDeviceFmodEx400::s_pFrameProfiler_Buffer_Other; 
CFrameProfiler* CAudioDeviceFmodEx400::s_pFrameProfiler_Buffer_Release; 
CFrameProfiler* CAudioDeviceFmodEx400::s_pFrameProfiler_Event_Other;
CFrameProfiler* CAudioDeviceFmodEx400::s_pFrameProfiler_Get_Param_By_Type;
CFrameProfiler* CAudioDeviceFmodEx400::s_pFrameProfiler_Set_Param_By_Type;
CFrameProfiler* CAudioDeviceFmodEx400::s_pFrameProfiler_Create_Sound;
CFrameProfiler* CAudioDeviceFmodEx400::s_pFrameProfiler_Start_Sound;
CFrameProfiler* CAudioDeviceFmodEx400::s_pFrameProfiler_Stop_Sound;

// global critical section for the AudioDevice
CryCriticalSection m_CSAD_Projects;

#ifdef FMOD_MEMORY_DEBUGGING
CAudioDeviceFmodEx400::tMemMap CAudioDeviceFmodEx400::MemMap;
CDebugLogger CAudioDeviceFmodEx400::MemLogger("FMOD_Memory.log");
char	CAudioDeviceFmodEx400::MemString[128];

//////////////////////////////////////////////////////////////////////////
void CAudioDeviceFmodEx400::TraceMemory(void *ptr, int nSize, int nMemMode)
{

	//CryAutoCriticalSection SmartCriticalSection(m_CS);

	tMemMap::iterator It = MemMap.find(ptr);

	switch(nMemMode)
	{
	case 1:	// alloc
				 {
					 if (It == MemMap.end())
					 {
						 sprintf_s(MemString,"Alloc   : %d Size: %d", ptr, nSize);
						 MemLogger.LogString(MemString,true);
						 MemMap[ptr] = nSize;
					 }
					 else
					 {
						 sprintf_s(MemString,"Alloc   : %d Size: %d - Already found!", ptr, nSize);
						 MemLogger.LogString(MemString,true);
					 }
				 }
				 break;
	case 2: 	// free
				 {
					 if (It == MemMap.end())
					 {
						 sprintf(MemString,"Free    : %d Size: %d - Not found!", ptr, nSize);
						 MemLogger.LogString(MemString,true);
					 }
					 else
					 {
						 sprintf(MemString,"Free    : %d Size: %d", ptr, nSize);
						 MemLogger.LogString(MemString,true);
						 MemMap.erase(It);			
					 }
				 }
				 break;
	default:
		break;
	}
}

#endif

#ifdef FMOD_MEMORY_DEBUGGING_SIMPLE
uint32 CAudioDeviceFmodEx400::nFMODMemUsage = 0;
uint32 CAudioDeviceFmodEx400::nFMODMemTotalSmallAllocCount = 0;
uint32 CAudioDeviceFmodEx400::nFMODMemTotalLargeAllocCount = 0;
uint32 CAudioDeviceFmodEx400::nFMODMemActiveAllocCount = 0;
#endif

#ifdef FMOD_STREAMING_DEBUGGING
//uint32 CAudioDeviceFmodEx400::nFMODFileOpenCount = 0;
uint32 CAudioDeviceFmodEx400::s_nFMODFileReadCount = 0;
uint32 CAudioDeviceFmodEx400::s_nFMODFileSeekCount = 0;
uint32 CAudioDeviceFmodEx400::s_nFMODFileReadBandwidth = 0;
#endif

namespace FModMemory
{





















































	ILINE void AddMemoryOp( void *ptr, uint32 nSize ) {}	
	ILINE void RemoveMemoryOp( void *ptr ) {}

};

#include "CryMemoryAllocator.h" // [saschad] Required in case NOT_USE_CRY_MEMORY_MANAGER is defined

// [Alexey] I found biggest problems with WriteLocks in CryMalloc because of tons of FMOD allocations
// Ive decided made a special fmod allocator (thread safe) and after that this problem disappeared
// We still have a statistics because node_allocator uses CryMalloc with proper statistics counting
extern "C" 
{
#if !defined(PS3)
	node_alloc<eCryDefaultMalloc, true, 524288> g_Allocator;
#endif
	//////////////////////////////////////////////////////////////////////////
	static void * F_CALLBACK CrySound_Alloc (unsigned int size, FMOD_MEMORY_TYPE memtype, const char *sourcestr)
	{
		FRAME_PROFILER("CrySound_Alloc", GetISystem(), PROFILE_SOUND);

		void* pAlloc = NULL;

#ifdef USE_POOL_ALLOCATOR

//#define FMOD_MEMORY_NORMAL             0x00000000       /* Standard memory. */
//#define FMOD_MEMORY_STREAM_FILE        0x00000001       /* Stream file buffer, size controllable with System::setStreamBufferSize. */
//#define FMOD_MEMORY_STREAM_DECODE      0x00000002       /* Stream decode buffer, size controllable with FMOD_CREATESOUNDEXINFO::decodebuffersize. */
//#define FMOD_MEMORY_XBOX360_PHYSICAL   0x00100000       /* Requires XPhysicalAlloc / XPhysicalFree. */
//#define FMOD_MEMORY_PERSISTENT         0x00200000       /* Persistent memory. Memory will be freed when System::release is called. */
//#define FMOD_MEMORY_SECONDARY          0x00400000       /* Secondary memory. Allocation should be in secondary memory. For example RSX on the PS3. */
//#define FMOD_MEMORY_ALL                0xFFFFFFFF
		
		if ((memtype & FMOD_MEMORY_XBOX360_PHYSICAL) == 0)
		{			
				pAlloc = g_MemoryPoolPrimary.Allocate<void*>(size, CRYSOUNDMEMORY_ALLIGNMENT,"FMOD");
		}
#ifdef PROVIDE_SECONDARY_POOL
		else
		{
			size_t const nSecondSize = g_MemoryPoolSoundSecondary.MemSize();
			if (nSecondSize)
			{
				// Secondary Memory is Referenced.
				size_t const pAllocHandle	= g_MemoryPoolSoundSecondary.Allocate<size_t>(size, CRYSOUNDMEMORY_ALLIGNMENT);
				if (pAllocHandle)
				{
					pAlloc = g_MemoryPoolSoundSecondary.Resolve<void*>(pAllocHandle);
					g_MemoryPoolSoundSecondary.Item(pAllocHandle)->Lock();
				}
			}
		}
#endif
		assert(pAlloc);

#else


#ifdef FMOD_MEMORY_DEBUGGING
		CAudioDeviceFmodEx400::TraceMemory(pAlloc, size, 1);
#endif

		// On PS3, malloc is wrapped and forwards to a global node allocator
		// so use this one to prevent allocated but unused chunks in both node allocators



		pAlloc = g_Allocator.alloc(size);


#ifdef FMOD_MEMORY_DEBUGGING
		CAudioDeviceFmodEx400::TraceMemory(ptr, size, 1);
#endif

#ifdef FMOD_MEMORY_DEBUGGING_SIMPLE




		size_t nSize = g_Allocator.getSizeEx(pAlloc); 


		if (!nSize)
		{



			nSize = CryCrtSize(pAlloc);

			++CAudioDeviceFmodEx400::nFMODMemTotalLargeAllocCount;
		}
		else
		{
			++CAudioDeviceFmodEx400::nFMODMemTotalSmallAllocCount;
		}

		CAudioDeviceFmodEx400::nFMODMemUsage += nSize;
		
		++CAudioDeviceFmodEx400::nFMODMemActiveAllocCount;
#endif

		FModMemory::AddMemoryOp( ptr, size );
#endif

#ifdef INCLUDE_AUDIO_PRODUCTION_CODE

		// Now also update some statistics
		switch (memtype)
		{
		case FMOD_MEMORY_NORMAL:
			{
				++CSoundSystem::g_oSoundMemoryInfo.nMemTypeNormalCount;
				break;
			}
		case FMOD_MEMORY_STREAM_FILE:
			{
				++CSoundSystem::g_oSoundMemoryInfo.nMemTypeStreamFileCount;
				break;
			}
		case FMOD_MEMORY_STREAM_DECODE:
			{
				++CSoundSystem::g_oSoundMemoryInfo.nMemTypeStreamDecodeCount;
				break;
			}
		case FMOD_MEMORY_XBOX360_PHYSICAL:
			{
				++CSoundSystem::g_oSoundMemoryInfo.nMemTypeXBox360PhysicalCount;
				break;
			}
		case FMOD_MEMORY_PERSISTENT:
			{
				++CSoundSystem::g_oSoundMemoryInfo.nMemTypePersistentCount;
				break;
			}
		case FMOD_MEMORY_SECONDARY:
			{
				++CSoundSystem::g_oSoundMemoryInfo.nMemTypeSecondaryCount;
				break;
			}
		default:
			{
				break;
			}
		}

		float const fBucketPoolSizeInMB			= g_MemoryPoolPrimary.GetSmallAllocsSize()/1048576.0f;
		float const fPrimaryPoolSizeInMB		= g_MemoryPoolPrimary.MemSize()/1048576.0f - g_MemoryPoolPrimary.MemFree()/1048576.0f;
		if (fBucketPoolSizeInMB > CSoundSystem::g_oSoundMemoryInfo.fSoundBucketPoolMaxInMB)
			CSoundSystem::g_oSoundMemoryInfo.fSoundBucketPoolMaxInMB = fBucketPoolSizeInMB;

		if (fPrimaryPoolSizeInMB > CSoundSystem::g_oSoundMemoryInfo.fSoundPrimaryPoolMaxInMB)
			CSoundSystem::g_oSoundMemoryInfo.fSoundPrimaryPoolMaxInMB = fPrimaryPoolSizeInMB;

#ifdef PROVIDE_SECONDARY_POOL
		float const fSecondaryPoolSizeInMB	= g_MemoryPoolSoundSecondary.MemSize()/1048576.0f - g_MemoryPoolSoundSecondary.MemFree()/1048576.0f;
		if (fSecondaryPoolSizeInMB > CSoundSystem::g_oSoundMemoryInfo.fSoundSecondaryPoolMaxInMB)
			CSoundSystem::g_oSoundMemoryInfo.fSoundSecondaryPoolMaxInMB = fSecondaryPoolSizeInMB;
		CSoundSystem::g_oSoundMemoryInfo.nSoundSecondaryPoolAllocCountMax	= g_MemoryPoolSoundSecondary.FragmentCount();
#endif // PROVIDE_SECONDARY_POOL

		CSoundSystem::g_oSoundMemoryInfo.nSoundBucketPoolAllocCountMax		= g_MemoryPoolPrimary.GetSmallAllocsCount();
		CSoundSystem::g_oSoundMemoryInfo.nSoundPrimaryPoolAllocCountMax		= g_MemoryPoolPrimary.FragmentCount();

		size_t nAllocSize	= size;
		size_t nCount			= 0;
		nAllocSize >>= 1;

		while(nAllocSize)
		{
			nCount++;
			nAllocSize >>= 1;
		}

		++CSoundSystem::g_oSoundMemoryInfo.anAllocations[nCount >= SSoundMemoryInfo::eAllocationsArraySize ? SSoundMemoryInfo::eAllocationsArraySize - 1 : nCount];

#endif // INCLUDE_AUDIO_PRODUCTION_CODE

		return pAlloc;
	}

	//////////////////////////////////////////////////////////////////////////
	static void F_CALLBACK CrySound_Free (void* ptr, FMOD_MEMORY_TYPE memtype, const char *sourcestr)
	{
#ifdef USE_POOL_ALLOCATOR
		
		bool bFreed = false;

		if (memtype & FMOD_MEMORY_XBOX360_PHYSICAL)
		{
#ifdef PROVIDE_SECONDARY_POOL
			// Secondary memory is referenced, retrieve handle
			size_t const pAllocHandle = g_MemoryPoolSoundSecondary.AddressToHandle(ptr);

			size_t const nOldSize = g_MemoryPoolSoundSecondary.Size(pAllocHandle);
			bFreed = g_MemoryPoolSoundSecondary.Free(pAllocHandle);
#endif
		}
		else
		{
			bFreed = g_MemoryPoolPrimary.Free(ptr);
			assert(bFreed);
		}		

		assert(bFreed);

#else

		FModMemory::RemoveMemoryOp( ptr );

#ifdef FMOD_MEMORY_DEBUGGING_SIMPLE




		size_t nSize = g_Allocator.getSizeEx(ptr); 

		if (!nSize) 
			nSize = CryCrtSize(ptr);


		CAudioDeviceFmodEx400::nFMODMemUsage -= nSize;
		--CAudioDeviceFmodEx400::nFMODMemActiveAllocCount;
#endif

		// On PS3, malloc is wrapped and forwards to a global node allocator
		// so use this one to prevent allocated but unused chunks in both node allocators



		unsigned size = g_Allocator.deallocate(ptr);


#ifdef FMOD_MEMORY_DEBUGGING
		CAudioDeviceFmodEx400::TraceMemory(ptr, size, 2);
#endif
#endif



	}

	//////////////////////////////////////////////////////////////////////////
	static void * F_CALLBACK CrySound_Realloc (void *pReAlloc, unsigned int size, FMOD_MEMORY_TYPE memtype, const char *sourcestr)
	{

		void * pNewAlloc = NULL;
		bool bFreed = false;

#ifdef USE_POOL_ALLOCATOR

		if (memtype & FMOD_MEMORY_XBOX360_PHYSICAL)
		{
			// Secondary Memory is Referenced. To not loose the handle, more memory is allocated
			// and at the beginning the handle is saved. FMOD gets the adress after the handle
#ifdef PROVIDE_SECONDARY_POOL
			size_t pNewAllocHandle	=	g_MemoryPoolSoundSecondary.Allocate<uint32>(size + CRYSOUNDMEMORY_ALLIGNMENT, CRYSOUNDMEMORY_ALLIGNMENT);
			if (pNewAllocHandle)
			{
				pNewAlloc	=	g_MemoryPoolSoundSecondary.Resolve<void*>(pNewAllocHandle);

				// save the handle at the beginning
				*(size_t*) pNewAlloc = pNewAllocHandle;

				// re-target new FMOD ptr behind handle
				pNewAlloc = ((uint8*)pNewAlloc) + CRYSOUNDMEMORY_ALLIGNMENT;

				// retrieve old handle
				size_t pOldAllocHandle = *(size_t*)(((uint8*)pReAlloc) - CRYSOUNDMEMORY_ALLIGNMENT);
				size_t nOldSize = g_MemoryPoolSoundSecondary.Size(pOldAllocHandle);

				if (nOldSize)
				{
					//pNewAlloc	=	g_MemoryPoolSoundSecondary.Allocate<void*>(size, CRYSOUNDMEMORY_ALLIGNMENT,"FMOD");
					//unsigned int nOldSize = g_MemoryPoolSoundSecondary.Size(pReAlloc);

					memmove(pNewAlloc, pReAlloc, nOldSize - CRYSOUNDMEMORY_ALLIGNMENT);

					bFreed = g_MemoryPoolSoundSecondary.Free(pOldAllocHandle);
				}
			}
#endif
		}
		else
		{
			pNewAlloc	=	g_MemoryPoolPrimary.Allocate<void*>(size, CRYSOUNDMEMORY_ALLIGNMENT,"FMOD");
			assert(pNewAlloc);

			if (pNewAlloc)
			{
				size_t const nOldSize = g_MemoryPoolPrimary.Size(pReAlloc);

				if (nOldSize)
				{
					memmove(pNewAlloc, pReAlloc, nOldSize);
					bFreed = g_MemoryPoolPrimary.Free(pReAlloc);
					assert(bFreed);
				}
			}
		}		

		assert(pNewAlloc);

#else
#ifdef FMOD_MEMORY_DEBUGGING
		void *pRealloc = NULL;
		unsigned int nOldsize = _msize( pReAlloc );
		CAudioDeviceFmodEx400::TraceMemory(pReAlloc, nOldsize, 2);
#endif

		// On PS3, malloc is wrapped and forwards to a global node allocator
		// so use this one to prevent allocated but unused chunks in both node allocators








		pNewAlloc = g_Allocator.alloc(size);
		size_t oldsize = g_Allocator.getSize(pReAlloc);
		memmove(pNewAlloc, pReAlloc, oldsize);
		g_Allocator.dealloc(pReAlloc);


#ifdef FMOD_MEMORY_DEBUGGING_SIMPLE
		CAudioDeviceFmodEx400::nFMODMemUsage = CAudioDeviceFmodEx400::nFMODMemUsage - oldsize + size;
		
		if (size > 512)
			++CAudioDeviceFmodEx400::nFMODMemTotalLargeAllocCount;
		else
			++CAudioDeviceFmodEx400::nFMODMemTotalSmallAllocCount;
#endif

#ifdef FMOD_MEMORY_DEBUGGING
		CAudioDeviceFmodEx400::TraceMemory(pRealloc, size, 1);
#endif
#endif

#ifdef INCLUDE_AUDIO_PRODUCTION_CODE

		// Now also update some statistics
		switch (memtype)
		{
		case FMOD_MEMORY_NORMAL:
			{
				++CSoundSystem::g_oSoundMemoryInfo.nMemTypeNormalCount;
				break;
			}
		case FMOD_MEMORY_STREAM_FILE:
			{
				++CSoundSystem::g_oSoundMemoryInfo.nMemTypeStreamFileCount;
				break;
			}
		case FMOD_MEMORY_STREAM_DECODE:
			{
				++CSoundSystem::g_oSoundMemoryInfo.nMemTypeStreamDecodeCount;
				break;
			}
		case FMOD_MEMORY_XBOX360_PHYSICAL:
			{
				++CSoundSystem::g_oSoundMemoryInfo.nMemTypeXBox360PhysicalCount;
				break;
			}
		case FMOD_MEMORY_PERSISTENT:
			{
				++CSoundSystem::g_oSoundMemoryInfo.nMemTypePersistentCount;
				break;
			}
		case FMOD_MEMORY_SECONDARY:
			{
				++CSoundSystem::g_oSoundMemoryInfo.nMemTypeSecondaryCount;
				break;
			}
		default:
			{
				break;
			}
		}

		float const fBucketPoolSizeInMB			= g_MemoryPoolPrimary.GetSmallAllocsSize()/1048576.0f;
		float const fPrimaryPoolSizeInMB		= g_MemoryPoolPrimary.MemSize()/1048576.0f - g_MemoryPoolPrimary.MemFree()/1048576.0f;

		if (fBucketPoolSizeInMB > CSoundSystem::g_oSoundMemoryInfo.fSoundBucketPoolMaxInMB)
			CSoundSystem::g_oSoundMemoryInfo.fSoundBucketPoolMaxInMB = fBucketPoolSizeInMB;

		if (fPrimaryPoolSizeInMB > CSoundSystem::g_oSoundMemoryInfo.fSoundPrimaryPoolMaxInMB)
			CSoundSystem::g_oSoundMemoryInfo.fSoundPrimaryPoolMaxInMB = fPrimaryPoolSizeInMB;
#ifdef PROVIDE_SECONDARY_POOL
		float const fSecondaryPoolSizeInMB	= g_MemoryPoolSoundSecondary.MemSize()/1048576.0f - g_MemoryPoolSoundSecondary.MemFree()/1048576.0f;
		if (fSecondaryPoolSizeInMB > CSoundSystem::g_oSoundMemoryInfo.fSoundSecondaryPoolMaxInMB)
			CSoundSystem::g_oSoundMemoryInfo.fSoundSecondaryPoolMaxInMB = fSecondaryPoolSizeInMB;
		CSoundSystem::g_oSoundMemoryInfo.nSoundSecondaryPoolAllocCountMax	= g_MemoryPoolSoundSecondary.FragmentCount();
#endif // PROVIDE_SECONDARY_POOL
		CSoundSystem::g_oSoundMemoryInfo.nSoundBucketPoolAllocCountMax		= g_MemoryPoolPrimary.GetSmallAllocsCount();
		CSoundSystem::g_oSoundMemoryInfo.nSoundPrimaryPoolAllocCountMax		= g_MemoryPoolPrimary.FragmentCount();

		size_t nAllocSize	= size;
		size_t nCount			= 0;
		nAllocSize >>= 1;

		while(nAllocSize)
		{
			nCount++;
			nAllocSize >>= 1;
		}

		++CSoundSystem::g_oSoundMemoryInfo.anAllocations[nCount >= SSoundMemoryInfo::eAllocationsArraySize ? SSoundMemoryInfo::eAllocationsArraySize - 1 : nCount];

#endif // INCLUDE_AUDIO_PRODUCTION_CODE

		return pNewAlloc;
	}
}


//////////////////////////////////////////////////////////////////////////
// File callbacks for FMODEX.
//////////////////////////////////////////////////////////////////////////
  //typedef FMOD_RESULT (F_CALLBACK *FMOD_FILE_OPENCALLBACK)        (const char *name, int unicode, unsigned int *filesize, void **handle, void **userdata);
  //typedef FMOD_RESULT (F_CALLBACK *FMOD_FILE_CLOSECALLBACK)       (void *handle, void *userdata);
  //typedef FMOD_RESULT (F_CALLBACK *FMOD_FILE_READCALLBACK)        (void *handle, void *buffer, unsigned int sizebytes, unsigned int *bytesread, void *userdata);
  //typedef FMOD_RESULT (F_CALLBACK *FMOD_FILE_SEEKCALLBACK)        (void *handle, unsigned int pos, void *userdata);
  //typedef FMOD_RESULT (F_CALLBACK *FMOD_FILE_ASYNCREADCALLBACK)   (FMOD_ASYNCREADINFO *info, void *userdata)
  //typedef FMOD_RESULT (F_CALLBACK *FMOD_FILE_ASYNCCANCELCALLBACK) (void *handle, void *userdata)

	//////////////////////////////////////////////////////////////////////////
	FMOD_RESULT F_CALLBACK FMODFileOpenCallback(const char *name, int unicode, unsigned int *filesize, void **handle, void **userdata)
	{
		assert(name);
		assert(filesize);
		assert(handle);

		FMOD_RESULT nResult = FMOD_OK;
		CSoundSystem const* const pSoundSystem = static_cast<CSoundSystem const* const>(gEnv->pSoundSystem);

		// don't allow any new requests anymore (probably trying to shutdown or unload a level)
		if (!pSoundSystem->GetAllowReadRequests())
		{
			return FMOD_ERR_FILE_NOTFOUND;
		}

		char szPath[512];
		unsigned int nFileSize = gEnv->pCryPak->FGetSize(name);

		*filesize = nFileSize;

		if (nFileSize == 0)
		{
			CryAutoCriticalSection SmartCriticalSection(m_CSAD_Projects);

			// FMOD HACK START
			// Iterate through all known Projects and create correct path on demand 
			// as long as FMOD can not track individual FEV Paths internaly if the file is loaded from memory
			CAudioDeviceFmodEx400* pAudioDevice = (CAudioDeviceFmodEx400*)gEnv->pSoundSystem->GetIAudioDevice();

			CAudioDeviceFmodEx400::VecProjectFilesIter IterEnd = pAudioDevice->m_VecLoadedProjectFiles.end();
			for (CAudioDeviceFmodEx400::VecProjectFilesIter Iter = pAudioDevice->m_VecLoadedProjectFiles.begin(); Iter!=IterEnd; ++Iter)
			{
				string sPath = PathUtil::GetPath((*Iter).sProjectName.c_str());
				sprintf_s(szPath, "%s%s", sPath.c_str(), name);
				nFileSize = gEnv->pCryPak->FGetSize(szPath);

				// FMOD HACK START
				if (nFileSize > 0)
					break; // leave the loop
			}

			if (nFileSize == 0)
				nResult = FMOD_ERR_FILE_NOTFOUND;	
		}
		else
		{
			size_t nLen = strlen(name);

			if (nLen < 512)
				sprintf_s(szPath, "%s", name);
			else
			{
				assert(!"FMOD file name is too long to process!");
				nResult = FMOD_ERR_FILE_NOTFOUND;	
			}
		}

		if (nResult == FMOD_ERR_FILE_NOTFOUND)
		{
#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
			// Make this known to the AFCM so this entry will be displayed properly in the "Not Cached Requests" column
			CFileCacheManager* const pFileCacheManager = pSoundSystem->GetFileCacheManager();
			
			if (pFileCacheManager)
			{
				pFileCacheManager->CreateFileCacheEntry(szPath, eAFCT_NOTCACHED);
			}
#endif // INCLUDE_AUDIO_PRODUCTION_CODE

			return FMOD_ERR_FILE_NOTFOUND;
		}

		// raw filesystem mapping
		if (pSoundSystem->g_nFileAccess == 0)
		{
			// File is opened for streaming.
			// rbx open flags, x is a hint to not cache whole file in memory.
			FILE *file = gEnv->pCryPak->FOpen( szPath,"rbx",ICryPak::FOPEN_HINT_DIRECT_OPERATION );

			if (!file)
				return FMOD_ERR_FILE_NOTFOUND;	

			*handle		= file;
			*filesize = nFileSize;
			++s_nFileOpenCount;

			return FMOD_OK;
		}

		// Streaming Engine mapping
		if (pSoundSystem->g_nFileAccess == 1)
		{
			// Music file?
			stack_string sTemp(szPath);
			sTemp = sTemp.substr(0, 6);
			bool const bIsMusic = sTemp.compareNoCase("music/") == 0;

			// Streamed file?
			sTemp = szPath;
			sTemp = sTemp.substr(sTemp.size()-8, 8);
			bool const bIsStream = sTemp.compareNoCase("_str.fsb") == 0;

			CFileCacheManager* const pFileCacheManager = pSoundSystem->GetFileCacheManager();
			assert(pFileCacheManager != NULL);

			if (pFileCacheManager)
			{
				CCryNameCRC const oNameCRC(szPath);

				if (bIsMusic && g_SoundCVars.g_nAllowMusicFileCaching == 1)
				{
					// Try to cache a music file if there's room.
					pFileCacheManager->CacheFile(szPath, oNameCRC, eAFCT_MUSIC);
				}
				else if (bIsStream && g_SoundCVars.g_nAllowStreamFileCaching == 1)
				{
					// Cache FSB header to minimize number of reads to a streamed soundbank.
					if (nFileSize < FSB_MAX_HEADER_CACHE_SIZE)
					{
						pFileCacheManager->CacheFile(szPath, oNameCRC, eAFCT_FSB_HEADER);
					}
					else
					{
						pFileCacheManager->CacheFile(szPath, oNameCRC, eAFCT_FSB_HEADER, 0, FSB_MAX_HEADER_CACHE_SIZE);
					}
				}

				// Retrieve handle and file size.
				bool bSuccess = pFileCacheManager->FindFileCacheEntry(oNameCRC, handle, filesize);

				if (!bSuccess)
				{
					// Create a new entry if the file wasn't found!
					pFileCacheManager->CreateFileCacheEntry(szPath, eAFCT_NOTCACHED);

					// And retrieve the handle and file size.
					bSuccess = pFileCacheManager->FindFileCacheEntry(oNameCRC, handle, filesize);
					assert(bSuccess);
				}

				// This is a global counter to give each event an unique ID.
				// With this ID reads can be properly associated and therefore the correct ones get addressed.
				++s_nOpenFileID;
				*userdata = reinterpret_cast<void*>(s_nOpenFileID);
				bSuccess  = pFileCacheManager->CreateEventSlot(oNameCRC, s_nOpenFileID);
				assert(bSuccess);
			}

			return FMOD_OK;
		}

		return FMOD_ERR_FILE_NOTFOUND;
	}

	//////////////////////////////////////////////////////////////////////////
	FMOD_RESULT F_CALLBACK FMODFileCloseCallback(void *nFile, void *userdata)
	{
		CSoundSystem const* const pSoundSystem = static_cast<CSoundSystem const* const>(gEnv->pSoundSystem);

		// raw filesystem mapping
		if (pSoundSystem->g_nFileAccess == 0)
		{
			assert(nFile);
			if (nFile)
			{
				FILE *file = (FILE*)nFile;

				if (!gEnv->pCryPak->FClose( file ))
				{
					--s_nFileOpenCount;
					return FMOD_OK;
				}
			}

			return FMOD_ERR_FILE_NOTFOUND;
		}

		// Streaming Engine mapping.
		if (pSoundSystem->g_nFileAccess == 1)
		{
			CFileCacheManager* const pFileCacheManager = pSoundSystem->GetFileCacheManager();

			if (pFileCacheManager)
			{
				pFileCacheManager->RemoveEventSlot(nFile, reinterpret_cast<size_t>(userdata));
			}
			
			return FMOD_OK;
		}
		
		return FMOD_ERR_FILE_NOTFOUND;
	}

	//////////////////////////////////////////////////////////////////////////
	FMOD_RESULT F_CALLBACK FMODFileReadCallback(void *nFile, void *buffer, unsigned int sizebytes, unsigned int *bytesread, void *userdata)
	{
		CSoundSystem* pSoundSystem = (CSoundSystem*) gEnv->pSoundSystem;

		// raw filesystem mapping
		if (pSoundSystem->g_nFileAccess == 0)
		{
			assert(nFile);
			assert(buffer);
			assert(sizebytes);
			assert(bytesread);
			FILE *file = (FILE*)nFile;

      // make sure this thread has valid name in frame profiler
      if(IThreadTaskManager *pTTM = gEnv->pSystem->GetIThreadTaskManager())
      {
        const char * pTName = pTTM->GetThreadName(CryGetCurrentThreadId());
        if(!pTName || !pTName[0])
          pTTM->SetThreadName(CryGetCurrentThreadId(),"FMOD FRead Callback");
      }
			
			PROFILE_DISK_TASK_TYPE(eStreamTaskTypeSound);

			uint32 nBytesRead = gEnv->pCryPak->FReadRaw( buffer,1,sizebytes,file );
			*bytesread = nBytesRead;

#ifdef FMOD_STREAMING_DEBUGGING
			CAudioDeviceFmodEx400::s_nFMODFileReadBandwidth += nBytesRead;
			++CAudioDeviceFmodEx400::s_nFMODFileReadCount;
#endif

			if (nBytesRead != sizebytes)
				return FMOD_ERR_FILE_EOF;

			return FMOD_OK;
		}

		// Streaming Engine mapping not supported in Synced-Request
		if (pSoundSystem->g_nFileAccess == 1)
		{
			return FMOD_ERR_FILE_NOTFOUND;
		}

		return FMOD_ERR_FILE_NOTFOUND;
	}

	//////////////////////////////////////////////////////////////////////////
	FMOD_RESULT F_CALLBACK FMODFileSeekCallback(void *nFile, unsigned int pos, void *userdata)
	{
		CSoundSystem* pSoundSystem = (CSoundSystem*) gEnv->pSoundSystem;

#ifdef FMOD_STREAMING_DEBUGGING
		++CAudioDeviceFmodEx400::s_nFMODFileSeekCount;
#endif

		// raw filesystem mapping
		if (pSoundSystem->g_nFileAccess == 0)
		{
			assert(nFile);
			FILE *file = (FILE*)nFile;
			// mode not supplied
			int mode = 0;

			if (gEnv->pCryPak->FSeek( file,pos,mode ))
				return FMOD_ERR_FILE_COULDNOTSEEK;
			else
				return FMOD_OK;
		}

		// Streaming Engine mapping does not need Seeking
		if (pSoundSystem->g_nFileAccess == 1)
		{
			return FMOD_ERR_FILE_COULDNOTSEEK;
		}

		return FMOD_ERR_FILE_COULDNOTSEEK;
	}
	
	//////////////////////////////////////////////////////////////////////////
	FMOD_RESULT F_CALLBACK FMODFileAsyncReadCallback(FMOD_ASYNCREADINFO *AsyncInfo, void *userdata)
	{
		// evil evil hack :), prevent fmod from starting new streaming request during quit
		// since if quiting due to disc ejection, the fmod file thread will always want to read new data
		// but this fails and due to a race condition, the failed read is never returned.
		// needs to be fixed the prober way










		CSoundSystem const* const pSoundSystem = static_cast<CSoundSystem const* const>(gEnv->pSoundSystem);

		// don't allow any new requests anymore (probably trying to shutdown or unload a level)
		if (!pSoundSystem->GetAllowReadRequests())
		{
			gEnv->pSoundSystem->Log(eSLT_Error, "<Sound> AFCM: FMOD_ERR_FILE_BAD GetAllowReadRequests");
			AsyncInfo->result = FMOD_ERR_FILE_BAD;

			return FMOD_OK;
		}

		// Here the AFCM handles all read requests.
		if (pSoundSystem->g_nFileAccess == 1)
		{
			pSoundSystem->GetFileCacheManager()->ReadFileCacheEntry(static_cast<void* const>(AsyncInfo), reinterpret_cast<size_t>(userdata));
			
			return FMOD_OK;
		}

		gEnv->pSoundSystem->Log(eSLT_Error, "<Sound> AFCM: FMOD_ERR_FILE_BAD g_nFileAccess");
		AsyncInfo->result = FMOD_ERR_FILE_BAD;

		return FMOD_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	FMOD_RESULT F_CALLBACK FMODFileAsyncCancelCallback(void *handle, void *userdata)
	{
		CSoundSystem const* const pSoundSystem = static_cast<CSoundSystem const* const>(gEnv->pSoundSystem);

		// Streaming Engine mapping
		if (pSoundSystem->g_nFileAccess == 1)
		{
			CFileCacheManager* const pFileCacheManager = pSoundSystem->GetFileCacheManager();

			if (pFileCacheManager)
			{
				pFileCacheManager->RemoveEventSlot(handle, reinterpret_cast<size_t>(userdata));
			}
		}
		return FMOD_OK;
	}
	
	//////////////////////////////////////////////////////////////////////////
	void CFMODFileStreamCallback::StreamAsyncOnComplete(IReadStream *pStream, unsigned int nError)
	{		
		FMOD_ASYNCREADINFO* const pAsyncInfo       = reinterpret_cast<FMOD_ASYNCREADINFO* const>(pStream->GetUserData());
		int unsigned const nBytesRead              = pStream->GetBytesRead();
		CSoundSystem* const pSoundSystem           = static_cast<CSoundSystem* const>(gEnv->pSoundSystem);
		CFileCacheManager* const pFileCacheManager = pSoundSystem->GetFileCacheManager();

		if (pFileCacheManager && nError != ERROR_USER_ABORT)
		{
			// Remove only if we didn't abort ourselves!
			pFileCacheManager->RemoveReadStreamOnEntry(pAsyncInfo->handle, pStream);
		}

#ifdef FMOD_STREAMING_DEBUGGING
		{
			CryAutoCriticalSection SmartCriticalSectionDebug(m_CSDebug);
			--g_nStreamingDebugCount;
#ifdef FMOD_STREAMING_DEBUG_LOGGING
			//gEnv->pSoundSystem->Log(eSLT_Always, "AD_ASYNC_SOC: %s (%d)", pEntry->sFilename.c_str(), g_nStreamingDebugCount);
#endif // FMOD_STREAMING_DEBUG_LOGGING
		}
#endif // FMOD_STREAMING_DEBUGGING

		// set bytes read before setting asyc info
		pAsyncInfo->bytesread = nBytesRead;

		// Important - Setting result in AsyncInfo MUST be the very last thing to do
		if (nError == 0)
		{
			if (nBytesRead == pAsyncInfo->sizebytes)
			{
				pAsyncInfo->result = FMOD_OK;
			}
			else if (nBytesRead)
			{
				pAsyncInfo->result = FMOD_ERR_FILE_EOF;
			}
			else
			{
				pSoundSystem->Log(eSLT_Error, "<Sound> AFCM: FMOD_ERR_FILE_BAD nBytesRead==0");
				pAsyncInfo->result = FMOD_ERR_FILE_BAD;
			}
		}
		else
		{
			// In case of any error make sure to set "FMOD_ERR_FILE_BAD" as this allows FMOD to free data.
			// With "FMOD_ERR_FILE_DISKEJECTED" we could resume streams which we currently don't support/need.
			pAsyncInfo->result = FMOD_ERR_FILE_BAD;
			pSoundSystem->Log(eSLT_Error, "<Sound> AFCM: FMOD_ERR_FILE_BAD CFMODFileStreamCallback::StreamAsyncOnComplete (error: %d)", nError);
		}

#ifdef FMOD_STREAMING_DEBUGGING
		CAudioDeviceFmodEx400::s_nFMODFileReadBandwidth += nBytesRead;
#endif
						
		// We cannot have an unsatisfied stream at this time
		assert(pAsyncInfo->result != FMOD_ERR_NOTREADY);				
					
	}

	//////////////////////////////////////////////////////////////////////////
	void CFMODFileStreamCallback::StreamOnComplete (IReadStream* pStream, unsigned int nError)
	{
		// leave empty because FMOD Streaming thread is controlling this, not the Main thread
	}

	//////////////////////////////////////////////////////////////////////////
	FMOD_RESULT F_CALLBACK CAudioDeviceFmodEx400::FMODThreadCallback(FMOD_SYSTEM *system, FMOD_SYSTEM_CALLBACKTYPE type, void* commanddata1, void* commanddata2)
	{
		FMOD::System *sys = (FMOD::System *)system;

		switch (type)
		{
		case FMOD_SYSTEM_CALLBACKTYPE_DEVICELISTCHANGED:
			{
				if (!gEnv->pSoundSystem->GetInterfaceExtended()->IsNullImplementation())
				{
					CAudioDeviceFmodEx400* const pAudioDevice = static_cast<CAudioDeviceFmodEx400* const>(gEnv->pSoundSystem->GetIAudioDevice());

					if (pAudioDevice)
					{
						pAudioDevice->SetDefaultDriver(true);
					}
				}
				else
				{
					CryFatalError("<Sound> Failed to set default audio driver during device list changed!");
				}

				break;
			}
		case FMOD_SYSTEM_CALLBACKTYPE_MEMORYALLOCATIONFAILED:
			{
				const char* sMemInfo = (const char*)commanddata1;
				int nBytes = (int)commanddata2;
				gEnv->pSoundSystem->Log(eSLT_Warning, "<Sound> Sound-FmodEx-AudioDevice Memory Allocation failed: %s %d bytes \n", sMemInfo, nBytes);
				//printf("ERROR : FMOD_SYSTEM_CALLBACKTYPE_MEMORYALLOCATIONFAILED occured.\n");
				//printf("%s.\n", commanddata1);
				//printf("%d bytes.\n", commanddata2);
				break;
			}
		case FMOD_SYSTEM_CALLBACKTYPE_THREADCREATED:
			{
				int nThreadID = (int)commanddata1;
				const char* sThreadName = (const char*)commanddata2;

				if (gEnv && gEnv->pSystem && gEnv->pLog)
				{
					gEnv->pSystem->GetIThreadTaskManager()->SetThreadName( nThreadID, sThreadName );
					gEnv->pLog->Log("<Sound> Sound-FmodEx-AudioDevice Thread created: %s (%d) \n", sThreadName, nThreadID);
				}

				//THREAD_HANDLE hThreadHandle = OpenThread(0, 0, nThreadID);
				//int threadPriority = GetThreadPriority( hThreadHandle );
				//CloseHandle(hThreadHandle);

				//SetThreadPriority( nThreadID, THREAD_PRIORITY_TIME_CRITICAL );
				break;
			}
		case FMOD_SYSTEM_CALLBACKTYPE_BADDSPCONNECTION:
			{
				FMOD::DSP *source = (FMOD::DSP *)commanddata1;
				FMOD::DSP *dest = (FMOD::DSP *)commanddata2;

				char sSource[256];
				char sDest[256];
				
				if (source)
					source->getInfo(sSource, 0,0,0,0);

				if (dest)
					dest->getInfo(sDest, 0,0,0,0);

				gEnv->pSoundSystem->Log(eSLT_Warning, "<Sound> Sound-FmodEx-AudioDevice bad DSP Connection occured: Source: %s Dest: %s \n", sSource, sDest);
				break;
			}
		}

		return FMOD_OK;



	}

//static int F_CALLBACKAPI CrySound_ftell( void * nFile )
//  {
//  FILE *file = (FILE*)nFile;
//  return gEnv->pCryPak->FTell( file );
//  }


	void CAudioDeviceFmodEx400::FmodErrorOutput(const char * sDescription, ESoundLogType LogType) const
	{
		gEnv->pSoundSystem->Log(LogType, "<Sound> FmodEx-AudioDevice: %s (%d) %s\n", sDescription, m_ExResult, FMOD_ErrorString(m_ExResult));
	}

void AddPhysicalBlock(long size);

CAudioDeviceFmodEx400::CAudioDeviceFmodEx400(void * hWnd)
: m_pnNumLoadsQueued(NULL),



  m_ExResult(FMOD_OK)
{
	m_nSpeakerMode			= FMOD_SPEAKERMODE_RAW;
	m_pEventSystem			= NULL;
	m_pSoundSystem			= NULL;
	m_pAPISystem				= NULL;
	m_pEventDumpLogger	= NULL;
	m_pCacheStaticMemBlock = NULL;
	m_nCacheStaticMemBlockSize = 0;




	m_tLastUpdate				= gEnv->pTimer->GetFrameStartTime();

#ifdef FMOD_STREAMING_DEBUGGING
	m_tLastBandwidthTime = gEnv->pTimer->GetFrameStartTime();
	//g_nFMODFileSeekCount = 0;
	m_fStreamKBPerSecond = 0.0f;
	//m_nFMODFileSeekCount = 0;
	//g_nFMODFileReadBandwidth = 0;
#endif
	//g_SoundFileIndex = 0;

//	nSoundMainThreadId = CryGetCurrentThreadId();

	s_pFrameProfiler_API_Info						= new CFrameProfiler( gEnv->pSystem, "FMOD API Info", PROFILE_SOUND ); 
	s_pFrameProfiler_API_Other					= new CFrameProfiler( gEnv->pSystem, "FMOD API Other", PROFILE_SOUND ); 
	s_pFrameProfiler_Buffer_Other				= new CFrameProfiler( gEnv->pSystem, "FMOD Buffer Other", PROFILE_SOUND ); 
	s_pFrameProfiler_Buffer_Release			= new CFrameProfiler( gEnv->pSystem, "FMOD Buffer Release", PROFILE_SOUND ); 
	s_pFrameProfiler_Event_Other				= new CFrameProfiler( gEnv->pSystem, "FMOD Event Other", PROFILE_SOUND );
	s_pFrameProfiler_Get_Param_By_Type	= new CFrameProfiler( gEnv->pSystem, "FMOD GetParamByType", PROFILE_SOUND );
	s_pFrameProfiler_Set_Param_By_Type	= new CFrameProfiler( gEnv->pSystem, "FMOD SetParamByType", PROFILE_SOUND );
	s_pFrameProfiler_Create_Sound				= new CFrameProfiler( gEnv->pSystem, "FMOD CreateSound", PROFILE_SOUND );
	s_pFrameProfiler_Start_Sound				= new CFrameProfiler( gEnv->pSystem, "FMOD StopSound", PROFILE_SOUND );
	s_pFrameProfiler_Stop_Sound					= new CFrameProfiler( gEnv->pSystem, "FMOD StartSound", PROFILE_SOUND );

	memset(&m_FMODMemory_Usage, 0, sizeof(FMOD_MEMORY_USAGE_DETAILS));

/*
// Deactivate unneeded CVAR code for now

#if defined(XENON)
	// set auto values for XBOX360
	g_nInternCacheStatic = 0; // turn Static Cache on by default (see x360.cfg) because XMA decoder can only work on physical memory
	g_fInternCacheSize = 24;
#elif defined(PS3)
	// set auto values for PS3
	g_nInternCacheStatic = 0; // turn Static Cache off by default (see ps3.cfg), until FMOD fixes memory tracking bug
	g_fInternCacheSize = 16;
#else
	// set auto values for PC
	g_nInternCacheStatic = 0; // off on PC by default
	g_fInternCacheSize = 60;
#endif

	ICVar* pCacheStatic = gEnv->pConsole->GetCVar("s_CacheStatic");
	ICVar* pCacheSize = gEnv->pConsole->GetCVar("s_CacheSize");

	float fCacheSizeCVar = pCacheSize ? pCacheSize->GetFVal() : 0.0f;
	bool bCacheStaticCVar = pCacheStatic ? (pCacheStatic->GetIVal() == 1 ) : false;

	m_InitSettings.fCacheSize = (fCacheSizeCVar > 0) ? fCacheSizeCVar : g_fInternCacheSize;
	m_InitSettings.bCacheStatic = (bCacheStaticCVar) ? bCacheStaticCVar : (g_nInternCacheStatic == 1);
*/

	m_InitSettings.fCacheSize = 0;
	m_InitSettings.bCacheStatic = 0;

	m_InitSettings.nHWChannels = 0;
	m_InitSettings.nSoftwareChannels = 0;
	m_InitSettings.nMPEGDecoders = 0;
	m_InitSettings.nXMADecoders = 0;
	m_InitSettings.nADPCMDecoders = 0;

	m_nCurrentMemAlloc = 0;
	m_nMaxMemAlloc		 = 0;
	
	m_nHardware2DChannels = 0;
	m_nHardware3DChannels = 0;
	m_nTotalHardwareChannelsAvail = 0;

  // 0 - 1, 1 being max volume
  m_fMasterVolume = 1.0f;
	m_nFMODDriverCaps = 0;
	m_nCountProject = 0;
	m_nCountEvent = 0;
	m_nCountGroup = 0;
	s_nFileOpenCount = 0;
	m_bNewProject = false;

  // store active window
  m_pHWnd = hWnd;

	m_nCurrentDebugLevel = -1;
	m_nStreamBufferSize = 0; // This will be overridden by g_nStreamingBufferSize

	m_VecLoadedProjectFiles.reserve(64);
}











































































CAudioDeviceFmodEx400::~CAudioDeviceFmodEx400()
{
	ShutDownDevice();

	IWavebank *pWavebank = NULL;
	tmapWavebanks::iterator ItEnd = m_Wavebanks.end();
	for (tmapWavebanks::const_iterator It = m_Wavebanks.begin(); It!=ItEnd; ++It)
	{
		pWavebank = (*It).second;

		if (pWavebank)
			POOL_FREE(pWavebank);
	}

	// Note: don't delete FrameProfiler as CrySystem takes care of cleaning up
	// s_pFrameProfiler_API_Info, s_pFrameProfiler_API_Other, s_pFrameProfiler_Buffer_Other, s_pFrameProfiler_Buffer_Release, s_pFrameProfiler_Event_Other, 

	delete[] m_pnNumLoadsQueued;
}

bool CAudioDeviceFmodEx400::InitDevice(CSoundSystem* pSoundSystem) 
{
  //GUARD_HEAP;

	if (!pSoundSystem) // check for valid system
		return (false);

	// Creating static memory blocks for XBOX360, PS3 or PC
	if (m_InitSettings.bCacheStatic && !m_pCacheStaticMemBlock)
	{
		// Currently static memory and cache size is deactive so we never go here
		MEMSTAT_CONTEXT(EMemStatContextTypes::MSC_SoundProject, 0, "Sound cache");
		m_nCacheStaticMemBlockSize = (uint32)(m_InitSettings.fCacheSize*1024*1024);





		m_pCacheStaticMemBlock = g_MemoryPoolPrimary.Allocate<void*>(m_nCacheStaticMemBlockSize, CRYSOUNDMEMORY_ALLIGNMENT,"FMOD");


//#if defined(XENON)
//		m_pCacheStaticMemBlock = XPhysicalAlloc(m_nCacheStaticMemBlockSize, MAXULONG_PTR, 0, PAGE_READWRITE | MEM_LARGE_PAGES);
//#else
//		// PC or PS3 static main memory block
//		m_pCacheStaticMemBlock = new int8[ m_nCacheStaticMemBlockSize ];
//#endif

		//memset(m_pCacheStaticMemBlock, 0, m_nCacheStaticMemBlockSize);

		if (m_pCacheStaticMemBlock)
		{
			m_ExResult = FMOD::Memory_Initialize(m_pCacheStaticMemBlock, m_nCacheStaticMemBlockSize, 0, 0, 0);
			// Added statistics for static memory block
			AddPhysicalBlock(m_nCacheStaticMemBlockSize);
			CryLogAlways("<Sound> Initialize FMOD with static memory block of %.0f\n", m_InitSettings.fCacheSize);
		}

	}

	if (!m_pCacheStaticMemBlock)
	{
		CryLogAlways("<Sound> Initialize FMOD with dynamic memory callbacks\n");
		m_ExResult = FMOD::Memory_Initialize(0, 0, CrySound_Alloc, CrySound_Realloc, CrySound_Free);
	}

	if (IS_FMODERROR)
	{
		FmodErrorOutput("memory initialization failed! ", eSLT_Error);
	}

	int nFMODCurrent = 0;
	int nMax = 0;
	m_ExResult = FMOD::Memory_GetStats(&nFMODCurrent, &nMax);

	//m_ExResult = FMOD::System_SetDebugMode(FMOD::DEBUG_FILE);
	//m_ExResult = FMOD::System_SetDebugLevel(FMOD::LOG_NONE);














	m_pSoundSystem = pSoundSystem;
  bool bTemp = true;
  m_nMemoryStatInc = 0;

  // system starts out not muted
  m_bMuteStatus = false;

  // make true when have new attributes for listener
  m_bHaveListenerAttributes = false;

  m_bSystemPaused = false; // starts out not paused
    
  // gets system stats when turned on
  m_bGetSystemStats = false;

	if (m_pSoundSystem->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
	{
		if (!m_CommandPlayer.IsRecording())
			m_CommandPlayer.RecordFile("SoundCommands.xml");

		m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::EVENTSYSTEM_CREATE, (int)gEnv->pTimer->GetFrameStartTime().GetMilliSeconds());
	}

	// 0 means <auto> values should be used
	m_InitSettings.nMPEGDecoders	= m_pSoundSystem->g_nMPEGDecoders ? m_pSoundSystem->g_nMPEGDecoders : m_pSoundSystem->g_nInternMPEGDecoders;
	m_InitSettings.nADPCMDecoders = m_pSoundSystem->g_nADPCMDecoders ? m_pSoundSystem->g_nADPCMDecoders : m_pSoundSystem->g_nInternADPCMDecoders;
	m_InitSettings.nXMADecoders		= m_pSoundSystem->g_nXMADecoders ? m_pSoundSystem->g_nXMADecoders : m_pSoundSystem->g_nInternXMADecoders;

	//m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::EVENTSYSTEM_GETSYSTEMOBJECT, gEnv->pTimer->GetFrameStartTime().GetMilliSeconds(), m_pEventSystem, m_pAPISystem);

	// if not already there, create again
	if (!m_pEventSystem)
	{
		//m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::EVENTSYSTEM_CREATE, gEnv->pTimer->GetFrameStartTime().GetMilliSeconds());
		m_ExResult = FMOD::EventSystem_Create(&m_pEventSystem);
		if (IS_FMODERROR)
		{
			FmodErrorOutput("event system failed! ", eSLT_Error);
			return false;
		}
	}

	// memory debugging
	//m_ExResult = m_pEventSystem->release();
	//if (IS_FMODERROR)
	//{
	//	FmodErrorOutput("event system release failed! ", eSLT_Error);
	//	bTemp = false;
	//}


	// Get FMOD-Ex System Ptr from EventSystem
	m_ExResult = m_pEventSystem->getSystemObject(&m_pAPISystem);
	if (IS_FMODERROR)
	{
		FmodErrorOutput("get system object failed! ", eSLT_Error);
		return false;
	}

	UpdateStreamBufferSize();

	//m_ExResult = m_pEventSystem->release();
	//if (IS_FMODERROR)
	//{
	//	FmodErrorOutput("event system release failed! ", eSLT_Error);
	//	bTemp = false;
	//}

	// and again..
	FMOD_DEBUGLEVEL nLevel = FMOD_DEBUG_LEVEL_NONE;
	
#ifdef CRYSOUNDSYSTEM_FMOD_RELEASE_WITH_LOGGING
	nLevel = FMOD_DEBUG_LEVEL_ALL;
#endif

	m_ExResult = FMOD::Debug_SetLevel(nLevel);

#ifdef INCLUDE_FMOD_EVENTNET_SYSTEM
	if (pSoundSystem->g_nNetworkAudition)
	{
		m_ExResult = FMOD::NetEventSystem_Init(m_pEventSystem);
		if (IS_FMODERROR)
		{
			FmodErrorOutput("event system network audition init failed! ", eSLT_Error);
			return false;
		}
	}
#endif // INCLUDE_FMOD_EVENTNET_SYSTEM

	// Get FMOD-Ex System Ptr from EventSystem
	if (m_pSoundSystem->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
		m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::EVENTSYSTEM_GETSYSTEMOBJECT, (int)gEnv->pTimer->GetFrameStartTime().GetMilliSeconds());

	m_ExResult = m_pEventSystem->getSystemObject(&m_pAPISystem);
	if (IS_FMODERROR)
	{
		FmodErrorOutput("get system object failed! ", eSLT_Error);
		return false;
	}

	m_ExResult = m_pAPISystem->setCallback(FMODThreadCallback);
	if (IS_FMODERROR)
	{
		FmodErrorOutput("set thread callback failed! ", eSLT_Error);
		//return false;
	}

	// changing number of Mpeg codec instances
	FMOD_ADVANCEDSETTINGS settings = {0};
	settings.cbsize = sizeof(FMOD_ADVANCEDSETTINGS);

	m_ExResult = m_pAPISystem->getAdvancedSettings(&settings);
	if (IS_FMODERROR)
	{
		FmodErrorOutput("get system advanced settings failed! ", eSLT_Error);
		return false;
	}

	settings.cbsize = sizeof(FMOD_ADVANCEDSETTINGS);
	settings.maxMPEGcodecs	= m_InitSettings.nMPEGDecoders;
	settings.maxADPCMcodecs = m_InitSettings.nADPCMDecoders;
	settings.maxXMAcodecs		= m_InitSettings.nXMADecoders;
	settings.eventqueuesize	= 1000; // Default is 32
	//settings.minHRTFAngle		= 90;
	////settings.maxHRTFAngle   = 0;
	//settings.minHRTFFreq    = 4000;
	//settings.maxHRTFFreq		= 22050;

	m_ExResult = m_pAPISystem->setAdvancedSettings(&settings);
	if (IS_FMODERROR)
	{
		FmodErrorOutput("set system advanced settings failed! ", eSLT_Error);
		return false;
	}

	// output types
	FMOD_OUTPUTTYPE nOutput = FMOD_OUTPUTTYPE_AUTODETECT;

	if (pSoundSystem->g_nOutputConfig == 1)
	{
		nOutput = FMOD_OUTPUTTYPE_DSOUND;
		CryLogAlways("<Sound> Trying to initialize DirectSound output! \n");
	}
	if (pSoundSystem->g_nOutputConfig == 2)
	{
		nOutput = FMOD_OUTPUTTYPE_WAVWRITER;
		CryLogAlways("<Sound> Trying to initialize Wav-Writer output! \n");
	}
	if (pSoundSystem->g_nOutputConfig == 3)
	{
		nOutput = FMOD_OUTPUTTYPE_WAVWRITER_NRT;
		CryLogAlways("<Sound> Trying to initialize Wav-Writer_NRT output! \n");
	}

	// software format
	int nSampleRate = 0;
	FMOD_SOUND_FORMAT Format = FMOD_SOUND_FORMAT_NONE;
	int nOutputChannels = 0;
	int nInputChannels = 0;
	FMOD_DSP_RESAMPLER Resampler = FMOD_DSP_RESAMPLER_NOINTERP;
	int nBits = 0;
	m_ExResult = m_pAPISystem->getSoftwareFormat(&nSampleRate, &Format, &nOutputChannels, &nInputChannels, &Resampler, &nBits);

	if (IS_FMODERROR)
	{
		FmodErrorOutput("get software format failed! ", eSLT_Error);
		return false;
	}

	if (pSoundSystem->g_nFormatSampleRate > 0 && pSoundSystem->g_nFormatSampleRate <= 96000)
		nSampleRate = pSoundSystem->g_nFormatSampleRate;

	if (pSoundSystem->g_nFormatType >= 0 && (FMOD_SOUND_FORMAT) pSoundSystem->g_nFormatType < FMOD_SOUND_FORMAT_MAX)
		Format = (FMOD_SOUND_FORMAT) pSoundSystem->g_nFormatType;

	if (pSoundSystem->g_nFormatResampler >= 0 && (FMOD_DSP_RESAMPLER) pSoundSystem->g_nFormatResampler < FMOD_DSP_RESAMPLER_MAX)
		Resampler = (FMOD_DSP_RESAMPLER) pSoundSystem->g_nFormatResampler;

	m_ExResult = m_pAPISystem->setSoftwareFormat(nSampleRate, Format, 0, 8, Resampler);

	if (IS_FMODERROR)
	{
		FmodErrorOutput("set software format failed! ", eSLT_Error);
		return false;
	}

	if (m_pSoundSystem->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
		m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::SYSTEM_SETOUTPUT, (int)gEnv->pTimer->GetFrameStartTime().GetMilliSeconds(), ptParamUINT32((uint32)nOutput));

  m_ExResult = m_pAPISystem->setOutput(nOutput);
	if (IS_FMODERROR)
	{
		FmodErrorOutput("set sound output failed! ", eSLT_Error);
		return false;
	}

	// Create DSound System object on Vista for SoftDec audio support
	m_ExResult = m_pAPISystem->getOutput(&nOutput);
	if (IS_FMODERROR)
	{
		FmodErrorOutput("get sound output failed! ", eSLT_Error);
		return false;
	}

	switch (nOutput) 
	{
	case FMOD_OUTPUTTYPE_DSOUND:
		CryLogAlways("<Sound> Starting to initialize DirectSound output! \n");
		break;
	case FMOD_OUTPUTTYPE_WASAPI:
		CryLogAlways("<Sound> Starting to initialize Windows Audio Session API output! \n");
		break;
	case FMOD_OUTPUTTYPE_WINMM:
		CryLogAlways("<Sound> Starting to initialize Windows Multimedia output! \n");
		break;
	case FMOD_OUTPUTTYPE_XBOX360:
		CryLogAlways("<Sound> Starting to initialize XBox360 output! \n");
		break;
	case FMOD_OUTPUTTYPE_PS3:
		CryLogAlways("<Sound> Starting to initialize PS3 output! \n");
		break;
	case FMOD_OUTPUTTYPE_WIIU:
		CryLogAlways("<Sound> Starting to initialize WiiU output! \n");
		break;
	default:
		break;
	}

	bool const bSuccess = SetDefaultDriver(false);

	if (!bSuccess)
	{
		return false;
	}

#if !defined(PS3) && !defined(XENON) && !defined(CAFE)
	// Set HW and Softwarevoices
	m_ExResult = m_pAPISystem->setHardwareChannels(m_InitSettings.nHWChannels);
	
	if (m_pSoundSystem->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
		m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::SYSTEM_SETHARDWARECHANNELS, (int)gEnv->pTimer->GetFrameStartTime().GetMilliSeconds(), ptParamUINT32(0), ptParamUINT32(0), ptParamUINT32(0), ptParamUINT32(0));
	
	//m_ExResult = m_pAPISystem->setHardwareChannels(0, 0, 0, 0);
	if (IS_FMODERROR)
	{
		FmodErrorOutput("set hardware voices limit failed! ", eSLT_Error);
		return false;
	}
#endif

	// overwrite file system
	if (pSoundSystem->g_nFileAccess == 0)
		m_ExResult = m_pAPISystem->setFileSystem(&FMODFileOpenCallback, &FMODFileCloseCallback, &FMODFileReadCallback, &FMODFileSeekCallback, 0 ,0, pSoundSystem->g_nBlockAlignSize);
	else
		m_ExResult = m_pAPISystem->setFileSystem(&FMODFileOpenCallback, &FMODFileCloseCallback, &FMODFileReadCallback, &FMODFileSeekCallback, &FMODFileAsyncReadCallback ,&FMODFileAsyncCancelCallback, pSoundSystem->g_nBlockAlignSize);

	if (IS_FMODERROR)
	{
		FmodErrorOutput("set file system callbacks failed! ", eSLT_Error);
		return false;
	}




	IsEax();


	m_ExResult = m_pAPISystem->setSpeakerMode(m_nSpeakerMode);

	if (IS_FMODERROR)
	{
		FmodErrorOutput("<Sound> Set speaker mode failed! ", eSLT_Error);
		return false;
	}

	// Print out the speaker mode.
	char const* pcSpeakerMode = NULL;

	switch (m_nSpeakerMode)
	{
	case FMOD_SPEAKERMODE_RAW:
		{
			pcSpeakerMode = "FMOD_SPEAKERMODE_RAW";

			break;
		}
	case FMOD_SPEAKERMODE_MONO:
		{
			pcSpeakerMode = "FMOD_SPEAKERMODE_MONO";

			break;
		}
	case FMOD_SPEAKERMODE_STEREO:
		{
			pcSpeakerMode = "FMOD_SPEAKERMODE_STEREO";

			break;
		}
	case FMOD_SPEAKERMODE_QUAD:
		{
			pcSpeakerMode = "FMOD_SPEAKERMODE_QUAD";

			break;
		}
	case FMOD_SPEAKERMODE_SURROUND:
		{
			pcSpeakerMode = "FMOD_SPEAKERMODE_SURROUND";

			break;
		}
	case FMOD_SPEAKERMODE_5POINT1:
		{
			pcSpeakerMode = "FMOD_SPEAKERMODE_5POINT1";

			break;
		}
	case FMOD_SPEAKERMODE_7POINT1:
		{
			pcSpeakerMode = "FMOD_SPEAKERMODE_7POINT1";

			break;
		}
	case FMOD_SPEAKERMODE_SRS5_1_MATRIX:
		{
			pcSpeakerMode = "FMOD_SPEAKERMODE_SRS5_1_MATRIX";

			break;
		}
	case FMOD_SPEAKERMODE_MYEARS:
		{
			pcSpeakerMode = "FMOD_SPEAKERMODE_MYEARS";

			break;
		}
	case FMOD_SPEAKERMODE_MAX:
		{
			pcSpeakerMode = "FMOD_SPEAKERMODE_MAX";

			break;
		}
	}

	CryLogAlways("<Sound> Using speaker mode: %s\n", pcSpeakerMode);

	if (m_pSoundSystem->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
		m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::SYSTEM_SETSPEAKERMODE, (int)gEnv->pTimer->GetFrameStartTime().GetMilliSeconds(), ptParamUINT32((uint32)m_nSpeakerMode));

	// TOMAS
	m_ExResult = m_pAPISystem->setSoftwareChannels(64);

	CryLogAlways("<Sound> Initializing FMOD-EX now!\n");
	
	if (m_pSoundSystem->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
		m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::EVENTSYSTEM_INIT, (int)gEnv->pTimer->GetFrameStartTime().GetMilliSeconds(), ptParamUINT32((uint32)m_InitSettings.nSoftwareChannels), ptParamUINT32((uint32)FMOD_INIT_NORMAL), ptParamUINT32(0), ptParamUINT32((uint32)FMOD_EVENT_INIT_NORMAL));

	FMOD_INITFLAGS InitFlags = FMOD_INIT_NORMAL;
	FMOD_EVENT_INITFLAGS EventInitFlags = FMOD_EVENT_INIT_NORMAL; //|FMOD_EVENT_INIT_DONTUSENAMES; // TODO:Enable NoName-Support

	if (m_pSoundSystem->g_nObstruction == 1)
		InitFlags |= FMOD_INIT_OCCLUSION_LOWPASS;

	if (m_pSoundSystem->g_nHRTF_DSP == 1)
		InitFlags |= FMOD_INIT_HRTF_LOWPASS;

	if (m_pSoundSystem->g_nVol0TurnsVirtual == 1)
		InitFlags |= FMOD_INIT_VOL0_BECOMES_VIRTUAL;

#ifdef INCLUDE_FMOD_EVENTNET_SYSTEM
	if (m_pSoundSystem->g_nNetworkAudition == 1)
		InitFlags |= FMOD_INIT_ENABLE_PROFILE;
#endif // INCLUDE_FMOD_EVENTNET_SYSTEM

	if (g_SoundCVars.g_nReverbType == REVERB_TYPE_SOFTWARE_LOW)
		InitFlags |= FMOD_INIT_SOFTWARE_REVERB_LOWMEM;

	// GUID conflicts should not cause any problems
	//EventInitFlags |= FMOD_EVENT_INIT_USE_GUIDS; // lets not use this for now

	// no names should be loaded
	//EventInitFlags |= FMOD_EVENT_INIT_DONTUSENAMES; // would break all name-based sound calls.

	// The listener angle event parameters will be affected by elevation, and not just horizontal components.
	EventInitFlags |= FMOD_EVENT_INIT_ELEVATION_AFFECTS_LISTENER_ANGLE;

	unsigned int nBlocksize = 0;
	int nNumblocks = 0;
	m_ExResult = m_pAPISystem->getDSPBufferSize(&nBlocksize, &nNumblocks);

#if !defined(PS3)
	if (m_pSoundSystem->g_nOutputConfig == 2 || m_pSoundSystem->g_nOutputConfig == 3)
	{
		m_ExResult = m_pEventSystem->init(m_InitSettings.nSoftwareChannels, InitFlags, (void*)"audio_capture.wav", EventInitFlags);
		if (IS_FMODERROR)
		{
			FmodErrorOutput("<Sound> EventSystem init failed piping into audio_capture.wav!", eSLT_Error);

			if (m_ExResult == FMOD_ERR_FILE_NOTFOUND)
			{
				FmodErrorOutput("<Sound> Make sure audio_capture.wav is not opened by another application!", eSLT_Error);
			}

			return false;
		}
	}
	else
#endif
	{
#if !defined(PS3)
		// test for windows advanced performance hardware acceleration slider
		if (m_nFMODDriverCaps & FMOD_CAPS_HARDWARE_EMULATED) // This is really bad for latency!. 
		{                                                   /* You might want to warn the user about this. */
			m_ExResult = m_pAPISystem->setDSPBufferSize(1024, 10);    /* At 48khz, the latency between issuing an fmod command and hearing it will now be about 213ms. */
			if (IS_FMODERROR)
			{
				FmodErrorOutput("system set DSP Buffer Size failed! ", eSLT_Error);
				//return false;
			}
		} 



#ifdef INCLUDE_FMOD_EVENTNET_SYSTEM
		m_ExResult = m_pEventSystem->init(m_InitSettings.nSoftwareChannels, InitFlags, m_pSoundSystem->g_nNetworkAudition?NULL:m_pHWnd, EventInitFlags);
#else
		m_ExResult = m_pEventSystem->init(m_InitSettings.nSoftwareChannels, InitFlags, NULL, EventInitFlags);
#endif // INCLUDE_FMOD_EVENTNET_SYSTEM



#endif // !PS3

		if (IS_FMODERROR)
		{
			FmodErrorOutput("event system init failed! ", eSLT_Error);
			if (m_ExResult == FMOD_ERR_OUTPUT_CREATEBUFFER)
			{
				// this might be an invalid speaker mode (5.0 or 7.1), so report an error if possible and fallback to stereo
				m_ExResult = m_pAPISystem->setSpeakerMode(FMOD_SPEAKERMODE_STEREO);
				
				if (IS_FMODERROR)
				{
					FmodErrorOutput("set fallback stereo speaker mode failed! ", eSLT_Error);
					return false;
				}

#ifdef INCLUDE_FMOD_EVENTNET_SYSTEM
				m_ExResult = m_pEventSystem->init(m_InitSettings.nSoftwareChannels, InitFlags, m_pSoundSystem->g_nNetworkAudition?NULL:m_pHWnd, FMOD_EVENT_INIT_NORMAL);
#else
				m_ExResult = m_pEventSystem->init(m_InitSettings.nSoftwareChannels, InitFlags, NULL, FMOD_EVENT_INIT_NORMAL);
#endif // INCLUDE_FMOD_EVENTNET_SYSTEM
				
				if (IS_FMODERROR)
				{
					FmodErrorOutput("event system init with fallback stereo speaker mode failed! ", eSLT_Error);
					return false;
				}
				else
					CryLogAlways("<Sound> Initialized FMOD-EX with stereo fallback\n");

			}
			else
				return false;
		}
		else
			CryLogAlways("<Sound> Initialized FMOD-EX\n");
	}
	
	uint32 nFMODSystemVersion;
	m_ExResult = m_pAPISystem->getVersion(&nFMODSystemVersion);
	CryLogAlways("<Sound> Using FMOD version: %08X and internal %08X!\n", nFMODSystemVersion, FMOD_VERSION);

	if (IS_FMODERROR)
	{
		FmodErrorOutput("Version number not available!", eSLT_Error);
		return false;
	}

	if (nFMODSystemVersion < FMOD_VERSION)
	{
		m_ExResult = FMOD_ERR_VERSION;
		if (IS_FMODERROR)
		{
			FmodErrorOutput("Version number conflict!", eSLT_Error);
			return false;
		}
	}

	// Create the version string
	uint32 nFMODEventVersion;
	m_ExResult = m_pEventSystem->getVersion(&nFMODEventVersion);
	m_sFMODSystemVersion.Format("%08X", nFMODSystemVersion);
	m_sFMODEventSystemVersion.Format("%08X", nFMODEventVersion);

	// Create the string for the FMOD system version
	CreateVersionString(m_sFMODSystemVersion);

	// Create the string for the FMOD event system version
	CreateVersionString(m_sFMODEventSystemVersion);

	// deactive SetMediaPath as long as FMOD can not track individual
	// FEV Paths internaly if the file is loaded from memory
	//char szPath[512];
	//sprintf_s(szPath, "/%s/Sounds/", PathUtil::GetGameFolder().c_str());
	////sprintf_s(szPath, "/Game/Sounds/");
	//
	//if (m_pSoundSystem->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
	//	m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::EVENTSYSTEM_SETMEDIAPATH, (int)gEnv->pTimer->GetFrameStartTime().GetMilliSeconds(), (void*) szPath);

	//m_ExResult = m_pEventSystem->setMediaPath(szPath);
	//if (IS_FMODERROR)
	//{
	//	FmodErrorOutput("invalid media path! ", eSLT_Warning);
	//}

	//m_ExResult = m_pFMODEX->setOutput(FMOD_OUTPUTTYPE_AUTODETECT);
	//m_ExResult = m_pFMODEX->setDriver(0);
	
	/*
	Set the distance units. (meters/feet etc).
	*/
	const float DISTANCEFACTOR = 1.0f;   // Units per meter.  I.e feet would = 3.28.  centimeters would = 100.
	// workaround for FMOD bug: distance attenuation was 1.0f, lowered to fix
	m_ExResult = m_pAPISystem->set3DSettings(1.0, DISTANCEFACTOR, 0.01f);
	if (IS_FMODERROR)
	{
		FmodErrorOutput("distance factor failed! ", eSLT_Warning);
	}

  // store the amount of software channels
  //ptParamINT32 softwareChannels(softwareChan);
  //bTemp = SetParam(adpSOFTWARECHANNELS ,&softwareChannels);
  
  //******  Init fmod system setting mixing/playing speed (44100)
  // also requesting 256 software channels
  // flags are -0- for now

  // After init called this sets up hardware channel info for 3d and 2d channels
  GetNumberSoundsPlaying();

  // report info to log file
	//int32 nNuMDriver;
	//char sDriverName[MAXCHARBUFFERSIZE];

	//m_ExResult = m_pCSEX->getDriver(&nNuMDriver);
	//m_ExResult = m_pCSEX->getDriverName(nNuMDriver, sDriverName, MAXCHARBUFFERSIZE);

	m_pSoundSystem->Log(eSLT_Always, "<Sound> -------------CRYSOUND-EX VERSION =   %08X ------- \n", FMOD_VERSION);
	m_ExResult = m_pAPISystem->getHardwareChannels(&m_nTotalHardwareChannelsAvail);
	m_pSoundSystem->Log(eSLT_Always, "<Sound> Total number of all hardware channels available: %d\n", m_nTotalHardwareChannelsAvail);

	//m_nHardware2DChannels = 5;
	//m_nHardware3DChannels = 5;
	//m_nTotalHardwareChannelsAvail = 10;

	IReverbManager *pReverbManager = m_pSoundSystem->GetIReverbManager();
	if (pReverbManager)
	{
		pReverbManager->Init(this, m_pSoundSystem);
		pReverbManager->SelectReverb(g_SoundCVars.g_nReverbType);
	}

	ISoundMoodManager* const pMoodManager = m_pSoundSystem->GetIMoodManager();
	if (pMoodManager)
	{
		pMoodManager->RefreshCategories();
	}
	
	g_SoundCVars.g_nNumLoadingThreadsToUse = clamp_tpl(g_SoundCVars.g_nNumLoadingThreadsToUse, 1, FMOD_MAX_NUM_NONBLOCKING_LOADING_THREADS);
	m_pnNumLoadsQueued                     = new size_t[g_SoundCVars.g_nNumLoadingThreadsToUse];
	memset(m_pnNumLoadsQueued, 0, static_cast<int>(sizeof(m_pnNumLoadsQueued)) * g_SoundCVars.g_nNumLoadingThreadsToUse);

	return bTemp;
}

void CAudioDeviceFmodEx400::GetInitSettings(AudioDeviceSettings *InitSettings)
{
	*InitSettings = m_InitSettings;
}

void CAudioDeviceFmodEx400::SetInitSettings(AudioDeviceSettings *InitSettings)
{
	m_InitSettings = *InitSettings;
}

bool CAudioDeviceFmodEx400::ShutDownDevice(void)
{
	MEMSTAT_LABEL("CAudioDeviceFmodEx400::ShutDownDevice");

	// in this function m_pSoundSystem might be NULL if initialization of SoundSystem failed

  bool bTemp=true;
  // this one dosn't need a param
  bTemp = SetParam(adpSTOPALL_CHANNELS, NULL);

	if (m_pEventSystem)
	{
#ifdef INCLUDE_FMOD_EVENTNET_SYSTEM
		if (m_pSoundSystem && m_pSoundSystem->g_nNetworkAudition)
		{
			m_ExResult = FMOD::NetEventSystem_Shutdown();
			if (IS_FMODERROR)
			{
				FmodErrorOutput("event system network audition shutdown failed! ", eSLT_Error);
				bTemp = false;
			}
		}
#endif // INCLUDE_FMOD_EVENTNET_SYSTEM
		
		// Unload projects, all handles turn invalid now!
		m_ExResult = m_pEventSystem->unload();

		assert(m_ExResult == FMOD_OK);


		// lets do one safety Update here, so FMOD can clean up and we might prevent a rare On-Exit crash on Xbox
		m_ExResult = m_pEventSystem->update();

		m_ExResult = m_pEventSystem->release();
		if (IS_FMODERROR)
		{
			FmodErrorOutput("event system release failed! ", eSLT_Error);
			bTemp = false;
		}

		m_pEventSystem = NULL;
		m_pAPISystem   = NULL;
	}

	int nFMODCurrent = 0;
	int nMax = 0;
	m_ExResult = FMOD::Memory_GetStats(&nFMODCurrent, &nMax);

	if (m_pSoundSystem->GetISoundAssetManager())
		m_pSoundSystem->GetISoundAssetManager()->RemoveProjectDependency();
	
	{
		CryAutoCriticalSection SmartCriticalSection(m_CSAD_Projects);

		for (VecProjectFilesIter it = m_VecLoadedProjectFiles.begin(), itEnd = m_VecLoadedProjectFiles.end(); it != itEnd; ++ it)
			delete [] it->pProjectFileInMemory;

		m_VecLoadedProjectFiles.clear();
	}

	for (std::vector<CPlatformSoundFmodEx400Event*>::const_iterator Iter(m_UnusedPlatformSoundEvents.begin()), IterEnd(m_UnusedPlatformSoundEvents.end()); Iter != IterEnd; ++Iter)
	{
		delete *Iter;
	}

	stl::free_container(m_UnusedPlatformSoundEvents);

	for (std::vector<CPlatformSoundFmodEx400*>::const_iterator Iter(m_UnusedPlatformSounds.begin()), IterEnd(m_UnusedPlatformSounds.end()); Iter != IterEnd; ++Iter)
	{
		delete *Iter;
	}

	stl::free_container(m_UnusedPlatformSounds);

	// refresh categories after unloading projects
	if (m_pSoundSystem && m_pSoundSystem->GetIMoodManager())
		m_pSoundSystem->GetIMoodManager()->RefreshCategories();

	// free static memory block in RSX on PS3








	// free static memory block in main memory
	if (m_pCacheStaticMemBlock)
	{



		delete[] (uint8*)(m_pCacheStaticMemBlock);

		m_pCacheStaticMemBlock = NULL;
		m_nCacheStaticMemBlockSize = 0;
	}

	return bTemp;
}

void CAudioDeviceFmodEx400::Reset(bool bForInit)
{
	if (bForInit)
	{
		if (m_UnusedPlatformSoundEvents.empty())
		{
			m_UnusedPlatformSoundEvents.reserve(m_pSoundSystem->g_nUnusedSoundCount);

			// prepare a user specified count of CPlatformSoundFmodEx400Event and store them in the m_UnusedPlatformSoundEvents container
			for (int i = 0; i < m_pSoundSystem->g_nUnusedSoundCount; ++i)
			{
				POOL_NEW_CREATE(CPlatformSoundFmodEx400Event, pNewPlatform)(NULL, m_pAPISystem, "Dummy");

				if (pNewPlatform)
					m_UnusedPlatformSoundEvents.push_back(pNewPlatform);
			}
		}

		m_UnusedPlatformSounds.reserve(m_pSoundSystem->g_nUnusedSoundCount);
	}
	else
	{
		for (std::vector<CPlatformSoundFmodEx400Event*>::iterator it = m_UnusedPlatformSoundEvents.begin(), itEnd = m_UnusedPlatformSoundEvents.end(); it != itEnd; ++ it)
			delete *it;

		stl::free_container(m_UnusedPlatformSoundEvents);

		for (std::vector<CPlatformSoundFmodEx400*>::iterator it = m_UnusedPlatformSounds.begin(), itEnd = m_UnusedPlatformSounds.end(); it != itEnd; ++ it)
			delete *it;

		stl::free_container(m_UnusedPlatformSounds);
	}
}

bool CAudioDeviceFmodEx400::ResetAudio(void)
{
  bool bTemp = true;
  //GUARD_HEAP;

	assert (m_pEventSystem);

	if (m_pSoundSystem->g_nUnloadData && m_pEventSystem)
	{
		m_pSoundSystem->GetISoundAssetManager()->RemoveProjectDependency();

		// Abort all in-flight streams before unloading projects, to ensure that we
		// don't stream into something that's about to be freed.
		CFileCacheManager* const pFileCacheManager = m_pSoundSystem->GetFileCacheManager();

		if (pFileCacheManager)
		{
			pFileCacheManager->AbortAllReadStreams();
		}

		size_t const nCount = m_VecLoadedProjectFiles.size();

		for (size_t i = 0; i < nCount; ++i)
		{
			UnloadProjectFile((ProjectIndex)i);
		}

		{	
			CryAutoCriticalSection SmartCriticalSection(m_CSAD_Projects);
			m_VecLoadedProjectFiles.clear();
		}
		
		// Unload projects, all handles turn invalid now!
		m_ExResult = m_pEventSystem->unload();

		assert(m_ExResult == FMOD_OK);

		// update group and event counting
		UpdateGroupEventCount();
	}

	// refresh categories after unloading projects
	if (m_pSoundSystem->GetIMoodManager())
		m_pSoundSystem->GetIMoodManager()->RefreshCategories();

	for (uint32 i=0; i<m_UnusedPlatformSoundEvents.size(); i++)
	{
		m_UnusedPlatformSoundEvents[i]->FreeMemory();
	}

	return bTemp;
}

// this is called every frame to update all listeners and must be called *before* SubSystemUpdate()
bool CAudioDeviceFmodEx400::UpdateListeners(void)
{
	if (!m_pAPISystem || !m_pEventSystem)
		return false;

	bool bResult = true;
	uint32 nNumActiveListeners = m_pSoundSystem->GetNumActiveListeners();
	FMOD_VECTOR vExPos;
	FMOD_VECTOR vExVel;
	FMOD_VECTOR vExForward;
	FMOD_VECTOR vExTop;

	if (nNumActiveListeners > 4)
	{
		m_ExResult = FMOD_ERR_INVALID_PARAM;
		FmodErrorOutput("more than 4 listeners! ", eSLT_Warning);
		nNumActiveListeners = 4;
	}


	CListener* pListener = NULL;
	ListenerID nListenerID = LISTENERID_INVALID;
	int nListenerIndex = 0;
	do 
	{
		pListener = (CListener*)m_pSoundSystem->GetNextListener(nListenerID);
		if (pListener)
		{
			nListenerID = pListener->GetID();

			if (pListener->GetActive())
			{
        Matrix34 const& rMatrix(pListener->GetMatrix());
        Vec3 const vPos(rMatrix.m03, rMatrix.m13, rMatrix.m23);
        Vec3 const vForward(rMatrix.TransformVector(Vec3(0.0f, 1.0f, 0.0f)).GetNormalized());
        Vec3 const vTop(rMatrix.TransformVector(Vec3(0.0f, 0.0f, 1.0f)).GetNormalized());
				
        ROUND_AUDIO_LISTENER_VECTOR(vExPos, vPos);
        ROUND_AUDIO_LISTENER_VECTOR(vExForward, vForward);
        ROUND_AUDIO_LISTENER_VECTOR(vExTop, vTop);

				if (m_pSoundSystem->g_nDoppler)
				{
          Vec3 const& rVel(pListener->GetVelocity());
          ROUND_AUDIO_LISTENER_VECTOR(vExVel, rVel);

#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
          if (rVel.GetLength() > 100.0f)
          {
            CryFixedStringT<32> sTemp;
            sTemp.Format("Listener exceeded 100 m/s! (%.2f)", rVel.GetLength());
            FmodErrorOutput(sTemp.c_str(), eSLT_Warning);
          }
#endif // INCLUDE_AUDIO_PRODUCTION_CODE
				}
				else
				{
					vExVel.x = 0.0f;
					vExVel.y = 0.0f;
					vExVel.z = 0.0f;
				}

				{
					if (m_pSoundSystem->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
						m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::EVENTSYSTEM_SET3DNUMLISTENERS, (int)gEnv->pTimer->GetFrameStartTime().GetMilliSeconds(), ptParamUINT32(nNumActiveListeners));

					CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_API_OTHER) );
					m_ExResult = m_pEventSystem->set3DNumListeners(nNumActiveListeners);
				}

				if (IS_FMODERROR)
				{
					FmodErrorOutput("event system invalid number of listeners! ", eSLT_Warning);
					bResult = false;
				}

				//m_pSoundSystem->Log("FMOD Update Listener Pos: %.2f,%.2f,%.2f", vPos.x, vPos.y, vPos.z);

				{	
					if (m_pSoundSystem->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
						m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::EVENTSYSTEM_SET3DLISTENERSATTRIBUTES, (int)gEnv->pTimer->GetFrameStartTime().GetMilliSeconds(), ptParamUINT32((uint32)nListenerID), ptParamVEC3F32(Vec3(vExPos.x, vExPos.y, vExPos.z)), ptParamVEC3F32(Vec3(vExVel.x, vExVel.y, vExVel.z)), ptParamVEC3F32(Vec3(vExForward.x, vExForward.y, vExForward.z)), ptParamVEC3F32(Vec3(vExTop.x, vExTop.y, vExTop.z)));

					CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_API_OTHER) );
					m_ExResult = m_pEventSystem->set3DListenerAttributes(nListenerIndex, &vExPos, &vExVel, &vExForward, &vExTop);
				}

				if (IS_FMODERROR)
				{
					FmodErrorOutput("event system listener 3d update failed! ", eSLT_Warning);
					bResult = false;
				}

				pListener->MarkAsSet();
				++nListenerIndex; // increase FMOD index
			}
		}

	}	while (pListener);

	return bResult;
}

// must be called every game frame
bool CAudioDeviceFmodEx400::Update(void)
{
	//float position[3];
	//float velocity[3];
	bool bResult = true;

	//CTimeValue tTimeDiff = gEnv->pTimer->GetAsyncTime() - m_pSoundSystem->m_tUpdateAudioDevice;
	//float fMilliSecs = tTimeDiff.GetMilliSeconds();
	//if (fMilliSecs == 0)
	//	return bResult;

	if (m_pSoundSystem)
	{
    float fVariationLimit = m_pSoundSystem->g_fVariationLimiter;












#ifdef FMOD_MEMORY_DEBUGGING_SIMPLE
		if (m_pSoundSystem->g_nDebugSound == SOUNDSYSTEM_DEBUG_MEMORY)
		{
			int nFMODCurrent = 0;
			m_ExResult = FMOD::Memory_GetStats(&nFMODCurrent, 0);
			m_pSoundSystem->Log(eSLT_Message, "<Sound> AD: Memory Allocs Active %d Total-small %d Total-large %d FMOD: %d Alloc-Control: %d\n", nFMODMemActiveAllocCount, nFMODMemTotalSmallAllocCount, nFMODMemTotalLargeAllocCount, nFMODCurrent, nFMODMemUsage);
		}

#endif

		if (m_pSoundSystem->g_nDebugSound != m_nCurrentDebugLevel)
		{
			m_nCurrentDebugLevel = m_pSoundSystem->g_nDebugSound;

			// This will work only with FMOD logging libraries (fmodL)
			FMOD_DEBUGLEVEL nLevel = FMOD_DEBUG_LEVEL_ALL;

			switch (m_pSoundSystem->g_nDebugSound)
			{
			case SOUNDSYSTEM_DEBUG_NONE:
				{
					nLevel = FMOD_DEBUG_LEVEL_NONE;
				}
				break;
			case SOUNDSYSTEM_DEBUG_FMOD_SIMPLE:
				{
					nLevel = (FMOD_DEBUG_TYPE_EVENT |
										FMOD_DEBUG_DISPLAY_TIMESTAMPS |
										FMOD_DEBUG_LEVEL_ERROR);
				}
				break;
			case SOUNDSYSTEM_DEBUG_FMOD_COMPLEX:
				{
					nLevel = (FMOD_DEBUG_TYPE_EVENT |
										FMOD_DEBUG_DISPLAY_TIMESTAMPS |
										FMOD_DEBUG_LEVEL_ERROR |
										FMOD_DEBUG_LEVEL_WARNING);
				}
				break;
			case SOUNDSYSTEM_DEBUG_FMOD_LOG:
				{
					nLevel = (FMOD_DEBUG_LEVEL_LOG |
										FMOD_DEBUG_DISPLAY_TIMESTAMPS);
				}
				break;
			case SOUNDSYSTEM_DEBUG_FMOD_MEMORY:
				{
					nLevel = (FMOD_DEBUG_TYPE_MEMORY |
										FMOD_DEBUG_LEVEL_HINT |
										FMOD_DEBUG_DISPLAY_TIMESTAMPS);
				}
				break;
			case SOUNDSYSTEM_DEBUG_FMOD_ALL:
				{
					nLevel = FMOD_DEBUG_LEVEL_ALL;
				}
				break;
			}

			m_ExResult = FMOD::Debug_SetLevel(nLevel);

			if (m_ExResult == FMOD_ERR_UNSUPPORTED)
			{
				m_pSoundSystem->Log(eSLT_Message, "<Sound> AD: Setting of FMOD debug level failed! The FMOD libraries do not support logging!");
			}

			if (m_pSoundSystem->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
			{
				if (!m_CommandPlayer.IsRecording())
					m_CommandPlayer.RecordFile("SoundCommands.xml");
			}
			else
			{
				m_CommandPlayer.StopRecording();
			}
		}

		UpdateStreamBufferSize();

		//DWORD threadid = GetCurrentThreadId();

		// every ? calls get stats (memory and cpu usage pct)
		//if (m_bGetSystemStats)
		//{
		//	if (++m_nMemoryStatInc >= GETMEMORYSTATSEVERY)
		//	{
		//		FRAME_PROFILER( "FMOD-GetMem_CPU", GetISystem(), PROFILE_SOUND );	
		//		GetCpuPctUsage();
		//		GetMemoryStats();
		//		m_nMemoryStatInc = 0;
		//	}
		//}

		int nUpdateLoops = 0;

		if (m_pSoundSystem->g_nOutputConfig == 3)
		{
			CTimeValue tCurrent = gEnv->pTimer->GetFrameStartTime();
			CTimeValue tTimeDiff = tCurrent - m_tLastUpdate;
			m_tLastUpdate = tCurrent;

			//CTimeValue tTimeDiff = (gEnv->pTimer->GetAsyncTime() - gEnv->pTimer->GetFrameStartTime()); //m_pSoundSystem->m_tUpdateAudioDevice;
			float fMS = tTimeDiff.GetMilliSeconds();

			assert (fMS > 0.0f);

			if (fMS > 0.0f && fMS < 5000.0f) // prevent long pause (5 sec) of silence when activated at runtime
			{
				while (abs(fMS) > 21.33f)
				{
					++nUpdateLoops;
					fMS -= 21.33f;
				}
				tTimeDiff.SetMilliSeconds((int64)fMS);
				m_tLastUpdate -= tTimeDiff;
			}

		}
		else
			nUpdateLoops = 1;

		// update crysound must be called every frame
		while (m_pEventSystem && nUpdateLoops>0)
		{	
			--nUpdateLoops;

			{
				if (m_pSoundSystem->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
					m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::EVENTSYSTEM_UPDATE, (int)gEnv->pTimer->GetFrameStartTime().GetMilliSeconds());

				FRAME_PROFILER( "SoundSystem::FMOD-EventUpdate", GetISystem(), PROFILE_SOUND );	
				m_ExResult = m_pEventSystem->update();
			}	

			if (IS_FMODERROR) 
			{
				// disable output for VS2
				FmodErrorOutput("event system update failed! ", eSLT_Warning);
				bResult = false;
			}

#ifdef INCLUDE_FMOD_EVENTNET_SYSTEM
			if (m_pSoundSystem->g_nNetworkAudition)
			{
				CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_API_OTHER) );
				m_ExResult = FMOD::NetEventSystem_Update();
				if (IS_FMODERROR)
				{
					FmodErrorOutput("event system network audition update failed! ", eSLT_Warning);
					bResult = false;
				}
			}
#endif // INCLUDE_FMOD_EVENTNET_SYSTEM

			if (m_pSoundSystem->g_nProfiling > 0)
			{
				// Ignore invalid file access for lookupdata because it shouldn't be written when we're profiling
				CDebugAllowFileAccess ignoreInvalidFileAccess;
				bool bSuccess = true;

				FMOD_EVENT_SYSTEMINFO SystemInfo;
				memset(&SystemInfo, 0, sizeof(FMOD_EVENT_SYSTEMINFO));

				// first get the number of wavebanks
				{
					CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_API_INFO) );
					m_ExResult = m_pEventSystem->getInfo(&SystemInfo);
				}
				if (IS_FMODERROR)
				{
					FmodErrorOutput("event system get system info failed! ", eSLT_Warning);
					bResult = bSuccess = false;
				}

				FMOD_EVENT_WAVEBANKINFO* pWaveBankInfos = NULL;

				if (SystemInfo.maxwavebanks)
				{
					// now repeat to fill the array of wavebank infos
					pWaveBankInfos = (FMOD_EVENT_WAVEBANKINFO*)g_MemoryPoolPrimary.Allocate<void*>(sizeof(FMOD_EVENT_WAVEBANKINFO)*SystemInfo.maxwavebanks, CRYSOUNDMEMORY_ALLIGNMENT,"FMOD");

					if (pWaveBankInfos)
					{
						SystemInfo.wavebankinfo = pWaveBankInfos;

						{
							CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_API_INFO) );
							m_ExResult = m_pEventSystem->getInfo(&SystemInfo);
						}
						if (IS_FMODERROR)
						{
							FmodErrorOutput("event system get system info with wavebanks failed! ", eSLT_Warning);
							bResult = bSuccess = false;
						}
					}
				}

				//nSizeInMB += Info.instancememory;

				if (bSuccess)
				{
					for (int i=0; i<SystemInfo.maxwavebanks; ++i)
					{
						FMOD_EVENT_WAVEBANKINFO oWaveBankInfo = SystemInfo.wavebankinfo[i];
						
						tmapWavebanks::iterator It = m_Wavebanks.find(CONST_TEMP_STRING(oWaveBankInfo.name));

						IWavebank *pWavebank = NULL;
						if (It == m_Wavebanks.end())
						{
							POOL_NEW(CWavebankFmodEx400, pWavebank)(oWaveBankInfo.name);

							if (!pWavebank)
							{
								POOL_FREE(pWaveBankInfos); // remove wavebankinfo array
								return false;
							}

							m_Wavebanks[oWaveBankInfo.name] = pWavebank;
						}
						else
							pWavebank = (*It).second;

						IWavebank::SWavebankInfo BankInfo;
						BankInfo.nMemCurrentlyInByte	= oWaveBankInfo.streammemory + oWaveBankInfo.samplememory;
						BankInfo.nMemPeakInByte				= oWaveBankInfo.streammemory + oWaveBankInfo.samplememory;

						if (*pWavebank->GetPath())
						{
							CCryFile file;

							m_sFullWaveBankName = "";

							int nPathLength = strlen(pWavebank->GetPath());
							int nNameLength = strlen(pWavebank->GetName());
							if (nPathLength + nNameLength + 4 < 512)
							{
								m_sFullWaveBankName = pWavebank->GetPath();
								m_sFullWaveBankName += pWavebank->GetName();
								m_sFullWaveBankName += ".fsb";

								if (file.Open( m_sFullWaveBankName, "rb" ))
									BankInfo.nFileSize = file.GetLength();
							}

						}
						pWavebank->AddInfo(BankInfo);

						//nSizeInMB += Info.wavebankinfo[i].streammemory + Info.wavebankinfo[i].samplememory;
					}
				}

				if (pWaveBankInfos)
					POOL_FREE(pWaveBankInfos); // remove wavebankinfo array
				
				//nSizeInMB = nSizeInMB/(1024*1024);

				//m_Wavebanks


			}

			if (m_pSoundSystem->g_nFindLostEvents)
				FindLostEvent();
		}

		// go through Projects and unload those which are not referenced
		if (m_pSoundSystem->g_nUnloadProjects)
		{
			CryAutoCriticalSection SmartCriticalSection(m_CSAD_Projects);

			VecProjectFilesIter IterEnd = m_VecLoadedProjectFiles.end();
			for (VecProjectFilesIter Iter = m_VecLoadedProjectFiles.begin(); Iter!=IterEnd; ++Iter)
			{
				if ((*Iter).pProjectHandle != PROJECTHANDLE_INVALID && (*Iter).pReadStream == 0 && (*Iter).nRefCount == 0)
				{
					CTimeValue tDeltaTime = gEnv->pTimer->GetFrameStartTime() - (*Iter).tLastTimeUsed;
					float fDiff = tDeltaTime.GetSeconds();

					if (fDiff > 20.0f)
					{
						// free preloaded FSBs first
						(*Iter).PreloadedFSBs.clear();

						if (g_nStreamingDebugCount == 0)
						{
							FMOD::EventProject* const pDeleteProject	= (FMOD::EventProject*)(*Iter).pProjectHandle;
							stack_string const sProjectName						= (*Iter).sProjectName;

							if (pDeleteProject)
							{
								m_ExResult	= pDeleteProject->cancelAllLoads();
								assert(m_ExResult == FMOD_OK);
								m_ExResult = pDeleteProject->release();

								assert(m_ExResult == FMOD_OK);

								if (m_pSoundSystem->g_nDebugSound == SOUNDSYSTEM_DEBUG_SIMPLE)
								{
									if (m_ExResult == FMOD_OK)
										m_pSoundSystem->Log(eSLT_Message, "<Sound> AD: Project released: %s", sProjectName.c_str());
									else
										m_pSoundSystem->Log(eSLT_Error, "<Sound> AD: Project release failed: %s", sProjectName.c_str());
								}

								if (m_ExResult != FMOD_OK)
									bResult = false;

								// update group and event counting
								UpdateGroupEventCount();
							}

							(*Iter).pProjectHandle	= PROJECTHANDLE_INVALID;
							(*Iter).nFileSize				= 0;
							break;
						}
					}
				}
			}
		}

		// go through Projects and load those which are not loaded in FMOD
		if (m_bNewProject)
		{
			CryAutoCriticalSection SmartCriticalSection(m_CSAD_Projects);

			bool bProjectWasRemoved = false;
			VecProjectFilesIter IterEnd = m_VecLoadedProjectFiles.end();
			for (VecProjectFilesIter Iter = m_VecLoadedProjectFiles.begin(); Iter!=IterEnd; ++Iter)
			{
				if ((*Iter).pProjectHandle == PROJECTHANDLE_INVALID && 
					(*Iter).pReadStream == 0 && 
					(*Iter).pProjectFileInMemory != 0 && 
					(*Iter).nFileSize > 0)
				{

					if (m_pSoundSystem->g_nDebugSound == SOUNDSYSTEM_DEBUG_SIMPLE)
						m_pSoundSystem->Log(eSLT_Message, "<Sound> AD: Project loaded: %s", (*Iter).sProjectName.c_str());

					FMOD_EVENT_LOADINFO Info;
					FMOD::EventProject *pProject = NULL;

					Info.size = sizeof(Info);
					Info.loadfrommemory_length = (*Iter).nFileSize;
					Info.sounddefentrylimit = fVariationLimit;
					//Info.sizeof_instancepool_simple = 50; //TODO find good value, if we have simple events

					// Set the relative project path to FMOD
					string sPath = PathUtil::GetPath((*Iter).sProjectName.c_str());
					m_ExResult = m_pEventSystem->setMediaPath(sPath.c_str());

					m_ExResult = m_pEventSystem->load((char*)(*Iter).pProjectFileInMemory, &Info, &pProject);

					if (m_pSoundSystem->g_nDebugSound == SOUNDSYSTEM_DEBUG_SIMPLE)
					{
						unsigned int nMemorySize = 1; // todo track memory difference
						m_pSoundSystem->Log(eSLT_Message, "<Sound> AD: Project <%s> File Size: %d Memory Size: %d", (*Iter).sProjectName.c_str(), Info.size, nMemorySize);
					}

					if (IS_FMODERROR)
					{
						FmodErrorOutput("load event project failed! " + (*Iter).sProjectName, eSLT_Error);
						assert(!"FMOD Project load failure!");
					}

					// FMOD creates a internal copy, so memory can be freed again
					delete[] (*Iter).pProjectFileInMemory;
					(*Iter).pProjectFileInMemory = NULL;
					(*Iter).nFileSize = 0;

					(*Iter).pProjectHandle = pProject; // pProject pointer will be NULL on load failure therefore the code below will be skipped

					// Preload whole Project
					if (pProject && m_pSoundSystem->g_nPreloadWeaponProjects)
					{
						int nFind = (*Iter).sProjectName.find("/w_", 3);
						bool bWeaponProject = (nFind > 0);

						if ((*Iter).bPreloadWholeProject && bWeaponProject)
						{
							FMOD::Event* pEvent = NULL;
							int nEventCount = 0;
							int nEventProjectID = 0;

							m_ExResult = pProject->getNumEvents(&nEventCount);

							if (nEventCount < 100)
							{
								string sWavebankName;
								string sWavebankNameFMOD;
								FMOD_EVENT_PROJECTINFO oProjectInfo;
								m_ExResult = pProject->getInfo(&oProjectInfo);

								int* pEventIDArray	=	(int*) g_MemoryPoolPrimary.Allocate<void*>(sizeof(int)*nEventCount, CRYSOUNDMEMORY_ALLIGNMENT,"FMOD");

								if (pEventIDArray)
								{
									// check which wavebanks are used in this project
									for (nEventProjectID=0; nEventProjectID<nEventCount; ++nEventProjectID)
									{
										pEventIDArray[nEventProjectID] = nEventProjectID;
										m_ExResult = pProject->getEventByProjectID(nEventProjectID, FMOD_EVENT_INFOONLY, &pEvent);

										if (pEvent)
										{
											FMOD_EVENT_INFO EventInfo;
											m_ExResult = pEvent->getInfo(0, 0, &EventInfo);

											if (EventInfo.maxwavebanks)
											{
												FMOD_EVENT_WAVEBANKINFO* pWaveBankInfos = (FMOD_EVENT_WAVEBANKINFO*) g_MemoryPoolPrimary.Allocate<void*>(sizeof(FMOD_EVENT_WAVEBANKINFO)*EventInfo.maxwavebanks, CRYSOUNDMEMORY_ALLIGNMENT,"FMOD");
												
												if (pWaveBankInfos)
												{
													EventInfo.wavebankinfo = pWaveBankInfos;
													m_ExResult = pEvent->getInfo(0, 0, &EventInfo);

													for (int i=0; i<EventInfo.maxwavebanks; ++i)
													{
														sWavebankNameFMOD = EventInfo.wavebankinfo[i].name;
														sWavebankNameFMOD += ".fsb";
														sWavebankName = "sounds/";
														sWavebankName += oProjectInfo.name;
														sWavebankName += "/";
														sWavebankName += sWavebankNameFMOD;

														CCryNameCRC const oWavebankCRC(sWavebankName.c_str());
														CSoundBuffer* pBuf = m_pSoundSystem->GetISoundAssetManager()->GetSoundBuffer(oWavebankCRC);


														if (!pBuf)
														{
															m_pSoundSystem->ParseNameIntoCache(sWavebankName.c_str());

															if (m_pSoundSystem->m_oParseNameCache.nReturnFlags)
															{
																//bool bOldValue			= m_bDelayPrecaching;
																//m_bDelayPrecaching	= false;
																pBuf = m_pSoundSystem->PrecacheSound(NULL, 0, FLAG_SOUND_PRECACHE_LOAD_SOUND);

																//m_bDelayPrecaching	= bOldValue;
															}
														}

														if (pBuf)
														{
															pBuf->GetProps()->sUserDataText = sWavebankNameFMOD.c_str(); // save raw fsb name for FMOD
															stl::push_back_unique((*Iter).PreloadedFSBs, pBuf);

															if (!pBuf->Loaded(false) && !pBuf->Loading())
															{
																bool bBufResult = pBuf->Load(NULL);
																pBuf->GetProps()->eBufferType = btSOUNDFSB;
															}

														}
													}
													bool bFreed = g_MemoryPoolPrimary.Free(pWaveBankInfos);
												}
											}
										}
									}

									//m_ExResult = pProject->loadSampleData(pEventIDArray, nEventCount, 0, 0, FMOD_NONBLOCKING);

									bool bFreed = g_MemoryPoolPrimary.Free(pEventIDArray);
								}
							}
						}
					}

					if (gEnv->IsEditor())
					{
						// refresh categories after loading a new project
						if (m_pSoundSystem->GetIMoodManager())
							m_pSoundSystem->GetIMoodManager()->RefreshCategories();
					}

					// informs about project availability
					if (m_pSoundSystem->GetISoundAssetManager())
						m_pSoundSystem->GetISoundAssetManager()->ProjectWasLoaded((*Iter).nProjectIndex);

					// update group and event counting
					UpdateGroupEventCount();
				}
			}
			
			m_bNewProject = false;
		}
	}
	
	return bResult;
}

void CAudioDeviceFmodEx400::GetMemoryStats(enumAudioDeviceMemoryQuery eMemoryQuery, unsigned int *nMemoryUsed)
{

	FRAME_PROFILER( "SoundSystem::FMOD-MemoryStats", GetISystem(), PROFILE_SOUND );	

	/*
#ifdef FMOD_MEMORY_DEBUGGING_SIMPLE
	if (nMemoryUsed)
		*nMemoryUsed = nFMODMemUsage;

	//if (nMaxOrCacheSize)
	//	*nMaxOrCacheSize = 0;

	//return nFMODMemUsage; // use own traced memory for now
	return;
#endif
	*/

	unsigned int nAllocated = 0;
	unsigned int nAllocated2 = 0;
	unsigned int nAllocatedSecondary = 0;
	int nAllocatedCurrent = 0;





	m_ExResult = FMOD::Memory_GetStats(&m_nCurrentMemAlloc, &m_nMaxMemAlloc);

	if (m_pAPISystem && m_pEventSystem)
	{

		switch (eMemoryQuery)
		{
		case admqALL:
			// use the cheaper version for now
			nAllocated = m_nCurrentMemAlloc;

			// if callback allocaltion was tracked, then this more accurate number is used
#ifdef FMOD_MEMORY_DEBUGGING_SIMPLE
			if (nFMODMemUsage)
				nAllocated = nFMODMemUsage;
#endif
			//m_ExResult = m_pEventSystem->getMemoryInfo(FMOD_MEMBITS_ALL, FMOD_EVENT_MEMBITS_ALL, &nAllocated, nMemoryUsedArray);
			break;

		case admqSOUNDALL:
			
			if (nAllocatedSecondary)
			{
				// query all sound data, we dont know if this is main or rsx
				m_pAPISystem->getSoundRAM(&nAllocatedCurrent, 0, 0); // use faster but a bit inaccurate function
				nAllocated = nAllocatedCurrent;
			}
			else
				m_ExResult = m_pEventSystem->getMemoryInfo(FMOD_MEMBITS_SOUND, 0, &nAllocated, &m_FMODMemory_Usage);
			break;

		case admqSOUNDMAIN:
			if (nAllocatedSecondary)
			{
				nAllocated = 0;
			}
			else
			{
				m_ExResult = m_pEventSystem->getMemoryInfo(FMOD_MEMBITS_SOUND, 0, &nAllocated, &m_FMODMemory_Usage);
			}

			break;

		case admqSOUNDSECONDARY:
			if (nAllocatedSecondary)
			{
				// query all sound data, we dont know if this is main or rsx
				m_pAPISystem->getSoundRAM(&nAllocatedCurrent, 0, 0); // use faster but a bit inaccurate function
				nAllocated = nAllocatedCurrent;
			}
			else
			{
				nAllocated = 0;
			}

			break;

		case admqSYSTEM:

			m_ExResult = m_pEventSystem->getMemoryInfo(FMOD_MEMBITS_ALL, FMOD_EVENT_MEMBITS_ALL, &nAllocated, &m_FMODMemory_Usage);
			m_ExResult = m_pEventSystem->getMemoryInfo(FMOD_MEMBITS_SOUND, 0, &nAllocated2, &m_FMODMemory_Usage);

			// everything except audio data
			if (nAllocated > nAllocated2)
				nAllocated = nAllocated - nAllocated2;

			break;
		case admqCACHEFILLALL:
			if (nAllocatedSecondary)
			{
				// query all sound data on PS3
				m_pAPISystem->getSoundRAM(&nAllocatedCurrent, 0, 0); // use faster but a bit inaccurate function
				nAllocated = nAllocatedCurrent;
			}
			nAllocated += m_nCurrentMemAlloc;
			break;
		case admqCACHEFILLMAIN:
			{
				nAllocated = m_nCurrentMemAlloc;
			}
			break;
		case admqCACHEFILLSECONDARY:
			if (nAllocatedSecondary)
			{
				// query all sound data on PS3
				m_pAPISystem->getSoundRAM(&nAllocatedCurrent, 0, 0); // use faster but a bit inaccurate function
				nAllocated = nAllocatedCurrent;
			}
			else
			{
				nAllocated = 0;
			}
			break;
		case admqCACHESIZEALL:
			if (m_InitSettings.bCacheStatic)
			{
				nAllocated = (unsigned int)(m_InitSettings.fCacheSize * 1024 * 1024) + nAllocatedSecondary;
			}
			else
			{
				ICVar* pCacheSize = gEnv->pConsole->GetCVar("s_CacheSize");
				float fCacheSizeCVar = pCacheSize ? pCacheSize->GetFVal() : 0.0f;
				fCacheSizeCVar = (fCacheSizeCVar > 0) ? fCacheSizeCVar : m_InitSettings.fCacheSize;
				nAllocated = (unsigned int)(fCacheSizeCVar * 1024 * 1024) + nAllocatedSecondary;
			}
			break;
		case admqCACHESIZEMAIN:
			if (m_InitSettings.bCacheStatic)
			{
				nAllocated = (unsigned int)(m_InitSettings.fCacheSize * 1024 * 1024);
			}
			else
			{
				ICVar* pCacheSize = gEnv->pConsole->GetCVar("s_CacheSize");
				float fCacheSizeCVar = pCacheSize ? pCacheSize->GetFVal() : 0.0f;
				fCacheSizeCVar = (fCacheSizeCVar > 0) ? fCacheSizeCVar : m_InitSettings.fCacheSize;
				nAllocated = (unsigned int)(fCacheSizeCVar * 1024 * 1024);
			}
			break;
		case admqCACHESIZESECONDARY:
			{
				nAllocated = nAllocatedSecondary;
			}
			break;
		}

	}

	if (IS_FMODERROR)
	{
		FmodErrorOutput("system get memory stats failed! ", eSLT_Warning);
		return;
	}
	
	if (nMemoryUsed)
	{
		*nMemoryUsed = nAllocated;
	}

	//if (nMaxOrCacheSize)
	//{
	//	// add static memblock
	//	if (m_pCacheStaticMemBlock)
	//		*nMaxOrCacheSize = m_nCacheStaticMemBlockSize;
	//	else
	//	{
	//		ICVar* pCacheSize = gEnv->pConsole->GetCVar("s_CacheSize");
	//		*nMaxOrCacheSize = pCacheSize ? pCacheSize->GetFVal()*1024*1024 : 0;
	//	}
	//}

	return;// m_nCurrentMemAlloc;
}

bool CAudioDeviceFmodEx400::IsEax(void)
{
	if (m_pAPISystem && !m_nFMODDriverCaps && m_pSoundSystem)
	{
		if (m_nSpeakerMode != FMOD_SPEAKERMODE_MAX)
			m_ExResult = m_pAPISystem->getDriverCaps(0, &m_nFMODDriverCaps, 0, &m_nSpeakerMode);
		else
			m_ExResult = m_pAPISystem->getDriverCaps(0, &m_nFMODDriverCaps, 0, 0);

		if (m_nFMODDriverCaps & FMOD_CAPS_HARDWARE_EMULATED)
			m_pSoundSystem->Log(eSLT_Always, " *WARNING* Hardware acceleration turned off sound performance settings!");

		if (m_nFMODDriverCaps & FMOD_CAPS_HARDWARE)
			m_pSoundSystem->Log(eSLT_Always, " Driver supports hardware 3D sound");
	}
	
	return false;
}

int  CAudioDeviceFmodEx400::GetNumberSoundsPlaying(void)
{
	int nTemp = 0;
	ptParamINT32 Param(nTemp);
	GetParam(adpCHANNELS_PLAYING, &Param);
	Param.GetValue(nTemp);

	return nTemp;
}

// percent of cpu usage by CrySound mixer,dsound, and streams
bool CAudioDeviceFmodEx400::GetCpuUsage(float *fDSP, float *fStream, float* fGeometry, float* fUpdate, float *fTotal)
{
	if (m_pAPISystem)
	{
		CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_API_INFO) );
		m_ExResult = m_pAPISystem->getCPUUsage(fDSP, fStream, fGeometry, fUpdate, fTotal);
	}

	if (IS_FMODERROR)
	{
		FmodErrorOutput("system get CPU usage failed! ", eSLT_Warning);
		return false;
	}

	return true;
}

// sets whole sound system's frequency
bool CAudioDeviceFmodEx400::SetFrequency(int newFreq)
{
  bool bTemp = true;

  m_nSystemFrequency = newFreq;

  //bTemp = (CS_SetFrequency(CS_ALL,newFreq) ? true : false);
  return bTemp;
}

// compute memory-consumption, returns rough estimate in MB
int CAudioDeviceFmodEx400::GetMemoryUsage(class ICrySizer* pSizer) const
{
	// TODO MAJOR REVIEW HERE !

	int nSizerSizeInByte = 0;
	int nWavebank = 0;

	if (pSizer)
	{		
		pSizer->AddObject(this, sizeof(*this));			
		pSizer->AddObject(m_UnusedPlatformSoundEvents);			
		pSizer->AddObject(m_UnusedPlatformSounds);	




	}

	// unused debug
	unsigned int nMemoryUsedEvent = 0;
	//unsigned int nMemoryUsedAPI = 0;
	m_ExResult = m_pEventSystem->getMemoryInfo(FMOD_MEMBITS_ALL, FMOD_EVENT_MEMBITS_ALL, &nMemoryUsedEvent, 0); // &m_FMODMemory_Usage);
	//m_ExResult = m_pAPISystem->getMemoryInfo(FMOD_MEMBITS_ALL, FMOD_EVENT_MEMBITS_ALL, &nMemoryUsedAPI, nMemoryUsedArray);
	// unused debug

	// API and Eventsystem both report the same numbers of sound data loaded
	//unsigned int nRSXMemoryUsedAPI = 0;
	//m_ExResult = m_pAPISystem->getMemoryInfo(FMOD_MEMBITS_SOUND, 0, &nRSXMemoryUsedAPI, 0);
	unsigned int nSoundMemoryUsedEvent = 0;
	m_ExResult = m_pEventSystem->getMemoryInfo(FMOD_MEMBITS_SOUND, 0, &nSoundMemoryUsedEvent, 0);

	//CSoundBuffer *pBuffer=m_pSoundBuffer;
	int nFMODCurrent = 0;
	int nMax = 0;
	m_ExResult = FMOD::Memory_GetStats(&nFMODCurrent, &nMax);
	
	if (IS_FMODERROR)
	{
		FmodErrorOutput("system get memory stats failed! ", eSLT_Warning);
		return 0;
	}

	// substract sound data stored in RSX memory









	// add static memblock
	if (m_pCacheStaticMemBlock)
	{
		// if we have a memblock, then FMODCurrent is part of the memblock
		nFMODCurrent = m_nCacheStaticMemBlockSize;
	}

	int nTempMemory = 0;

	if (pSizer)
	{
		SIZER_SUBCOMPONENT_NAME(pSizer, "FMOD Heap");

//#ifdef FMOD_MEMORY_DEBUGGING_SIMPLE
//		nSizerSizeInByte += nFMODMemUsage;
//		pSizer->AddObject(m_pAPISystem, nFMODMemUsage);
//#else
//		nSizerSizeInByte += nFMODCurrent;
//		pSizer->AddObject(m_pAPISystem, nFMODCurrent);
//#endif
		const unsigned int nMemTypeCount = 48;
		static const char* memTypes[nMemTypeCount] = {
			"FMOD_MEMTYPE_OTHER",
			"FMOD_MEMTYPE_STRING",
			"FMOD_MEMTYPE_SYSTEM",
			"FMOD_MEMTYPE_PLUGINS",
			"FMOD_MEMTYPE_OUTPUT",
			"FMOD_MEMTYPE_CHANNEL",
			"FMOD_MEMTYPE_CHANNELGROUP",
			"FMOD_MEMTYPE_CODEC",
			"FMOD_MEMTYPE_FILE",
			"FMOD_MEMTYPE_SOUND",
			"FMOD_MEMTYPE_SOUNDARRAYRAM",
			"FMOD_MEMTYPE_SOUNDGROUP",
			"FMOD_MEMTYPE_STREAMBUFFER",
			"FMOD_MEMTYPE_DSPCONNECTION",
			"FMOD_MEMTYPE_DSP",
			"FMOD_MEMTYPE_DSPCODEC",
			"FMOD_MEMTYPE_PROFILE",
			"FMOD_MEMTYPE_RECORDBUFFER",
			"FMOD_MEMTYPE_REVERB",
			"FMOD_MEMTYPE_REVERBCHANNELPROPS",
			"FMOD_MEMTYPE_GEOMETRY",
			"FMOD_MEMTYPE_SYNCPOINT",
			"FMOD_MEMTYPE_EVENTSYSTEM",
			"FMOD_MEMTYPE_MUSICSYSTEM",
			"FMOD_MEMTYPE_FEV",
			"FMOD_MEMTYPE_MEMORYFSB",
			"FMOD_MEMTYPE_EVENTPROJECT",
			"FMOD_MEMTYPE_EVENTGROUPI",
			"FMOD_MEMTYPE_SOUNDBANKCLASS",
			"FMOD_MEMTYPE_SOUNDBANKLIST",
			"FMOD_MEMTYPE_STREAMINSTANCE",
			"FMOD_MEMTYPE_SOUNDDEFCLASS",
			"FMOD_MEMTYPE_SOUNDDEFDEFCLASS",
			"FMOD_MEMTYPE_SOUNDDEFPOOL",
			"FMOD_MEMTYPE_REVERBDEF",
			"FMOD_MEMTYPE_EVENTREVERB",
			"FMOD_MEMTYPE_USERPROPERTY",
			"FMOD_MEMTYPE_EVENTINSTANCE",
			"FMOD_MEMTYPE_EVENTINSTANCE_COMPLEX",
			"FMOD_MEMTYPE_EVENTINSTANCE_SIMPLE",
			"FMOD_MEMTYPE_EVENTINSTANCE_LAYER",
			"FMOD_MEMTYPE_EVENTINSTANCE_SOUND",
			"FMOD_MEMTYPE_EVENTENVELOPE",
			"FMOD_MEMTYPE_EVENTENVELOPEDEF",
			"FMOD_MEMTYPE_EVENTPARAMETER",
			"FMOD_MEMTYPE_EVENTCATEGORY",
			"FMOD_MEMTYPE_EVENTENVELOPEPOINT",
			"FMOD_MEMTYPE_EVENTINSTANCEPOOL"
		};

		for (int mtIdx = 0; mtIdx != static_cast<int>(nMemTypeCount); ++ mtIdx)
		{




			{
				//SIZER_SUBCOMPONENT_NAME(pSizer, memTypes[mtIdx]);
				//pSizer->AddObject(m_pAPISystem + mtIdx, m_FMODMemory_Usage[mtIdx]);
				//nSizerSizeInByte += m_FMODMemory_Usage[mtIdx];
			}
		}

		//pSizer->AddObject(m_pAPISystem, nFMODCurrent); // use own traced memory
		//pSizer->AddObject(m_pEventSystem, Info.instancememory);
	}

	FMOD_EVENT_SYSTEMINFO SystemInfo;

	// first get the number of wavebanks
	m_ExResult = m_pEventSystem->getInfo(&SystemInfo);
	if (IS_FMODERROR)
	{
		FmodErrorOutput("event system get system info failed! ", eSLT_Warning);
		return 0;
	}

	FMOD_EVENT_WAVEBANKINFO* pWavebankInfo = NULL;

	if (SystemInfo.maxwavebanks)
	{
		// now repeat to fill the array of wavebank infos
		pWavebankInfo = new FMOD_EVENT_WAVEBANKINFO[SystemInfo.maxwavebanks];

		if (!pWavebankInfo)
			return false;

		SystemInfo.wavebankinfo = pWavebankInfo;

		m_ExResult = m_pEventSystem->getInfo(&SystemInfo);
		if (IS_FMODERROR)
		{
			FmodErrorOutput("event system get system info with wavebanks failed! ", eSLT_Warning);
			delete[] pWavebankInfo; // remove wavebankinfo array
			return 0;
		}
	}

	//nTempMemory = SystemInfo.eventmemory + SystemInfo.instancememory + SystemInfo.dspmemory;

	for (int i=0; i<SystemInfo.maxwavebanks; ++i)
	{
		nWavebank += pWavebankInfo[i].streammemory + pWavebankInfo[i].samplememory;
		nTempMemory += pWavebankInfo[i].streammemory + pWavebankInfo[i].samplememory;
		//if (pSizer)
		//pSizer->AddObject(&(Info.wavebankinfo[i]), Info.wavebankinfo[i].streammemory + Info.wavebankinfo[i].samplememory);
	}

	delete[] pWavebankInfo; // remove wavebankinfo array

#ifdef FMOD_MEMORY_DEBUGGING
	int nNum = MemMap.size();
	int nSum = 0;
	tMemMap::iterator ItEnd = MemMap.end();
	for (tMemMap::iterator It = MemMap.begin(); It!=ItEnd; ++It)
	{
		nSum += (*It).second;
	}
#endif



	return nSizerSizeInByte;
}

// accesses wavebanks
IWavebank* CAudioDeviceFmodEx400::GetWavebank(int nIndex)
{
	IWavebank *pWavebank = NULL;

	int nIdx=0;
	tmapWavebanks::iterator ItEnd = m_Wavebanks.end();
	for (tmapWavebanks::const_iterator It = m_Wavebanks.begin(); It!=ItEnd; ++It)
	{
		if (nIndex == nIdx)
			pWavebank = (*It).second;
		++nIdx;
	}

	return pWavebank;
}

// accesses wavebanks
IWavebank* CAudioDeviceFmodEx400::GetWavebank(const char* sWavebankName)
{
	IWavebank *pWavebank = NULL;

	tmapWavebanks::iterator It = m_Wavebanks.find(CONST_TEMP_STRING(sWavebankName));
	if (It != m_Wavebanks.end())
	{
			pWavebank = (*It).second;
	}

	return pWavebank;
}

//MVD
// creates a new platform dependent SoundBuffer
CSoundBuffer* CAudioDeviceFmodEx400::CreateSoundBuffer(const SSoundBufferProps &BufferProps)
{
	CSoundBuffer* pBuf = NULL;
	if (BufferProps.eBufferType == btEVENT)
	{
		POOL_NEW(CSoundBufferFmodEx400Event, pBuf)(BufferProps, m_pAPISystem);
	}

	if (BufferProps.eBufferType == btMICRO)
	{
		if (m_pSoundSystem->g_nRecordConfig == 0)
		{
			POOL_NEW(CSoundBufferFmodEx400Micro, pBuf)(BufferProps, m_pAPISystem, 0); // use default == 0 index
		}
		else
		{
			POOL_NEW(CSoundBufferFmodEx400Micro, pBuf)(BufferProps, m_pAPISystem, m_pSoundSystem->g_nRecordConfig-1); // decrease recordconfig because FMOD index starts with 0
		}
	}

	if (BufferProps.eBufferType == btNETWORK)
	{
		POOL_NEW(CSoundBufferFmodEx400Network, pBuf)(BufferProps, m_pAPISystem);
	}

	if (BufferProps.eBufferType == btSAMPLE || BufferProps.eBufferType == btSOUNDFSB)
	{
		POOL_NEW(CSoundBufferFmodEx400, pBuf)(BufferProps, m_pAPISystem);
	}

	return pBuf;
}

// creates a new platform dependent PlatformSound
IPlatformSound* CAudioDeviceFmodEx400::CreatePlatformSound(ISound* pSound, const char *sEventName)
{
	IPlatformSound* pPlatform = NULL;
	bool bResult = false;

	if (sEventName[0])
	{
		if (m_UnusedPlatformSoundEvents.empty())
		{
			POOL_NEW_CREATE(CPlatformSoundFmodEx400Event, pNewPlatform)(pSound, m_pAPISystem, sEventName);

			if (!pNewPlatform)
				return NULL;

			pPlatform = pNewPlatform;
		}
		else
		{
			pPlatform = m_UnusedPlatformSoundEvents[m_UnusedPlatformSoundEvents.size()-1];
			pPlatform->Reset(pSound, sEventName);
			m_UnusedPlatformSoundEvents.pop_back();
		}
	}
	else
	{
		if (m_UnusedPlatformSounds.empty())
		{
			POOL_NEW_CREATE(CPlatformSoundFmodEx400, pNewPlatform)(pSound, m_pAPISystem);

			if (!pNewPlatform)
				return NULL;

			pPlatform = pNewPlatform;
		}
		else
		{
			pPlatform = m_UnusedPlatformSounds[m_UnusedPlatformSounds.size()-1];
			pPlatform->Reset(pSound, "");
			m_UnusedPlatformSounds.pop_back();
		}
	}

	return pPlatform;
}

bool CAudioDeviceFmodEx400::RemovePlatformSound(IPlatformSound* pPlatformSound)
{
	if (pPlatformSound)
	{
		if (pPlatformSound->GetClass() == pscEVENT)
		{
			CPlatformSoundFmodEx400Event* const pPlatformSoundEvent = (CPlatformSoundFmodEx400Event*)pPlatformSound;

			// make sure to not push back a platformsound which still contains a event handle.
			// TODO find when a sound gets removed from the soundsystem while the event still fades out
			pPlatformSound->StopSound(true);
			pPlatformSound->FreeSoundHandle();

			int unsigned const nCountEvents = m_UnusedPlatformSoundEvents.size();
			if (nCountEvents < (unsigned int)m_pSoundSystem->g_nUnusedSoundCount)
				m_UnusedPlatformSoundEvents.push_back(pPlatformSoundEvent);
			else
			{
				delete pPlatformSoundEvent;
			}
		}
		else
		{
			CPlatformSoundFmodEx400* const pPlatformSoundTemp = (CPlatformSoundFmodEx400*)pPlatformSound;
			int unsigned const nCountSounds = m_UnusedPlatformSounds.size();
			if (nCountSounds < (unsigned int)m_pSoundSystem->g_nUnusedSoundCount)
				m_UnusedPlatformSounds.push_back(pPlatformSoundTemp);
			else
			{
				delete pPlatformSoundTemp;
			}
		}
	}

	return true;
}

// check for multiple record devices and expose them, write name to sName pointer
bool CAudioDeviceFmodEx400::GetRecordDeviceInfo(const int nRecordDevice, char* sName, const int nNameLength)
{
	if (m_pAPISystem)
	{
		CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_API_OTHER) );
		m_pAPISystem->getRecordDriverInfo(nRecordDevice, sName, nNameLength, 0);
		if (IS_FMODERROR)
		{
			FmodErrorOutput("retrieve microphone record information failed! ", eSLT_Warning);
			return false;
		}
	}
	return true;
}


/*
adpSOFTWARECHANNELS,
adpHARDWARECHANNELS,
adpTOTALCHANNELS,
adpEAX_STATUS,
adpMUTE_STATUS,
adpLISTENER_POSITION,
adpLISTENER_VELOCITY,
adpLISTENER_FORWARD,
adpLISTENER_TOP,
adpWINDOW_HANDLE,
adpSYSTEM_FREQUENCY,
adpMASTER_VOLUME,
adpMIXING_RATE,
adpSPEAKER_MODE,
adpPAUSED_STATUS,
adpSNDSYSTEM_MEM_USAGE_NOW,
adpSNDSYSTEM_MEM_USAGE_HIGHEST,
adpSNDSYSTEM_OUTPUTTYPE,
adpSNDSYSTEM_DRIVER
adpDOPPLER
*/
bool CAudioDeviceFmodEx400::GetParam(enumAudioDeviceParamSemantics enumtype, ptParam* pParam)
{
	int   nTemp = 0;
	float fTemp = 0.0f;
	bool  bTemp = false;;
	Vec3	vTemp;
	string sTemp;  
	void * vpTemp = NULL;
	float tempVect[3];
	bool result=true;


	switch(enumtype) 
	{
    case adpDOPPLER: // turn doppler on or off with a bool
      if (!(pParam->GetValue(bTemp))) 
				return (false);
      bTemp = m_bDopplerStatus;
      pParam->SetValue(bTemp);
      break;
    case adpHARDWARECHANNELS:// number of hardware channels available
      if (!(pParam->GetValue(nTemp))) 
				return (false);
      GetNumberSoundsPlaying();
      nTemp = m_nTotalHardwareChannelsAvail;
      pParam->SetValue(nTemp);
      break;
    case adpTOTALCHANNELS:// total number of channels available
      if (!(pParam->GetValue(nTemp))) 
				return (false);
      GetNumberSoundsPlaying();
      nTemp = m_nTotalHardwareChannelsAvail + m_InitSettings.nSoftwareChannels;
      pParam->SetValue(nTemp);
      break;
    case adpEAX_STATUS:// is eax available on this machine
      if (!(pParam->GetValue(bTemp))) 
				return (false);
      bTemp = IsEax();
      pParam->SetValue(bTemp);
      break;
    case adpMUTE_STATUS:// returns bool telling caller if system wide mute on=true or off=false
      if (!(pParam->GetValue(bTemp))) 
				return (false);
      bTemp = m_bMuteStatus;
      pParam->SetValue(bTemp);
      break;
    case adpLISTENER_POSITION:// gets active Listeners position
      if (!(pParam->GetValue(vTemp))) 
				return (false);
      tempVect[0] = tempVect[1] = tempVect[2] = 0.0f;
      //CS_3D_Listener_GetAttributes(tempVect, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
      vTemp(tempVect[0], tempVect[2], tempVect[1]);
      pParam->SetValue(vTemp);
      break;
    case adpLISTENER_VELOCITY:// gets active Listeners velocity
      if (!(pParam->GetValue(vTemp))) 
				return (false);
      tempVect[0] = tempVect[1] = tempVect[2] = 0.0f;
      //CS_3D_Listener_GetAttributes(NULL, tempVect, NULL, NULL, NULL, NULL, NULL, NULL);
      vTemp(tempVect[0], tempVect[2], tempVect[1]);
      pParam->SetValue(vTemp);
      break;
    case adpLISTENER_FORWARD:// gets active Listeners direction or forward
      if (!(pParam->GetValue(vTemp))) 
				return (false);
      tempVect[0] = tempVect[1] = tempVect[2] = 0.0f;
      //CS_3D_Listener_GetAttributes(NULL, NULL, &tempVect[0], &tempVect[1], &tempVect[2], NULL, NULL, NULL);
      vTemp(tempVect[0], tempVect[2], tempVect[1]);
      pParam->SetValue(vTemp);
      break;
    case adpLISTENER_TOP:// gets active Listeners top
      if (!(pParam->GetValue(vTemp))) 
				return (false);
      tempVect[0] = tempVect[1] = tempVect[2] = 0.0f;
      //CS_3D_Listener_GetAttributes(NULL, NULL, NULL, NULL, NULL, &tempVect[0], &tempVect[1], &tempVect[2]);
      vTemp(tempVect[0], tempVect[2], tempVect[1]);
      pParam->SetValue(vTemp);
      break;
    case adpWINDOW_HANDLE:// returns handle of active window which is void * type
      if (!(pParam->GetValue(vpTemp))) 
				return (false);
      vpTemp = m_pHWnd;
      pParam->SetValue(vpTemp);
      break;
    case adpSYSTEM_FREQUENCY:// gets the mixing speed
      if (!(pParam->GetValue(nTemp))) 
				return (false);
      //nTemp = CS_GetFrequency(CS_ALL);
      pParam->SetValue(nTemp);
			break;
		case adpMASTER_VOLUME: // value from 0-1, 1 being max fmod master volume
			{
				if (!(pParam->GetValue(fTemp))) 
					return (false);

				CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_API_OTHER) );
				FMOD::ChannelGroup *MasterChannelGroup = 0;
				m_ExResult = m_pAPISystem->getMasterChannelGroup(&MasterChannelGroup);
				if (!IS_FMODERROR)
				{
					m_ExResult = MasterChannelGroup->getVolume(&fTemp);
				}
				pParam->SetValue(nTemp);
				break;
			}
		case adpMASTER_PAUSE: // value true/false
			{
				if (!(pParam->GetValue(bTemp))) 
					return (false);

				CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_API_OTHER) );
				FMOD::ChannelGroup *MasterChannelGroup = 0;
				m_ExResult = m_pAPISystem->getMasterChannelGroup(&MasterChannelGroup);
				if (!IS_FMODERROR)
				{
					m_ExResult = MasterChannelGroup->getPaused(&bTemp);
				}
				pParam->SetValue(nTemp);
				break;
			}
    case adpSPEAKER_MODE:// gets active speaker mode
			if (!(pParam->GetValue(nTemp))) 
				return (false);
			switch (m_nSpeakerMode)
			{
			case FMOD_SPEAKERMODE_MONO :
				nTemp = 1;
				break;
			case FMOD_SPEAKERMODE_STEREO :
				nTemp = 2;
				break;
			case FMOD_SPEAKERMODE_QUAD :
				nTemp = 4;
				break;
			case FMOD_SPEAKERMODE_SURROUND :
				nTemp = 5;
				break;
			case FMOD_SPEAKERMODE_5POINT1 :
				nTemp = 6;
				break;
			case FMOD_SPEAKERMODE_7POINT1 :
				nTemp = 8;
				break;
			case FMOD_SPEAKERMODE_SRS5_1_MATRIX :
				nTemp = 9;
				break;
			}
			pParam->SetValue(nTemp);
      break;
    case adpPAUSED_STATUS:// is fmod paused or not
      if (!(pParam->GetValue(bTemp))) 
				return (false);
      bTemp = m_bSystemPaused;
      pParam->SetValue(bTemp);
      break;
    case adpSNDSYSTEM_MEM_USAGE_NOW:// returns how much memory fmod is using RIGHT NOW
      if (!(pParam->GetValue(nTemp))) 
				return (false);
      GetMemoryStats(admqALL, 0);
      nTemp = m_nCurrentMemAlloc;
      pParam->SetValue(nTemp);
      break;
    case adpSNDSYSTEM_MEM_USAGE_HIGHEST:// returns the amount of memory fmod used at its PEEK
      if (!(pParam->GetValue(nTemp))) 
				return (false);
      GetMemoryStats(admqALL, 0);
      nTemp = m_nMaxMemAlloc;
      pParam->SetValue(nTemp);
      break;
    case adpSNDSYSTEM_OUTPUTTYPE:// returns a enum in int form of output type
      if (!(pParam->GetValue(nTemp))) 
				return (false);

			// TODO IMPLEMENT
      pParam->SetValue(nTemp);
      break;
    case adpSNDSYSTEM_DRIVER:// sets a string name of driver
      if (!(pParam->GetValue(sTemp))) 
				return (false);

			// TODO IMPLEMENT
      pParam->SetValue(sTemp);
      break;
    case adpMIXING_BUFFER_SIZE:// ****** OPTIONAL ****** if set returns mixing buffer size in milliseconds (fmod does automaticly)
      if (!(pParam->GetValue(nTemp))) 
				return (false);
      nTemp = m_nMixingBuffSize;
      pParam->SetValue(nTemp);
			break;
		case adpCHANNELS_PLAYING:// returns number of channels being used
			{
				if (!(pParam->GetValue(nTemp)) || !m_pAPISystem) 
					return (false);

				CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_API_INFO) );
				m_ExResult = m_pAPISystem->getChannelsPlaying(&nTemp);
				if (IS_FMODERROR)
				{
					FmodErrorOutput("system get channels playing failed! ", eSLT_Warning);
					return false;
				}

				pParam->SetValue(nTemp);
				return false; // disable FMOD-EX polling because event system is ignored (BUG)
				break;
			}
		case adpSTOPALL_CHANNELS:// ******* NOT ACTIVE !!!!!
			break;
		case adpEVENTCOUNT:
			if (!(pParam->GetValue(nTemp))) 
				return (false);
			nTemp = m_nCountEvent;
			pParam->SetValue(nTemp);
			break;
		case adpGROUPCOUNT:
			if (!(pParam->GetValue(nTemp))) 
				return (false);
			nTemp = m_nCountGroup;
			pParam->SetValue(nTemp);
			break;
    default:
      break;
    }
    return result;
	}
	bool CAudioDeviceFmodEx400::SetParam(enumAudioDeviceParamSemantics enumtype, ptParam* pParam)
	{
		int   nTemp = 0;
		float fTemp = 0.0f;
		bool  bTemp = false;
		Vec3	vTemp;
		string sTemp;  
		void * vpTemp = NULL;
		bool result = true;

		switch(enumtype) 
		{
		case adpHARDWARECHANNELS:
		case adpTOTALCHANNELS:
		case adpEAX_STATUS:          // these values are not used at this time
    case adpCHANNELS_PLAYING:
    case adpSNDSYSTEM_MEM_USAGE_NOW:
    case adpSNDSYSTEM_MEM_USAGE_HIGHEST:
      result = false;
      break;
    case adpMIXING_BUFFER_SIZE:// ****OPTIONAL**** set the size of the mixing buffer in milliSeconds 
      if (!(pParam->GetValue(nTemp))) 
				return (false);
      m_nMixingBuffSize = nTemp;
      //result = (CS_SetBufferSize(nTemp) ? true : false);
      break;
    case adpMUTE_STATUS:// changes the system mute status ,bTemp true mutes the system false reactivates sound system
      if (!(pParam->GetValue(bTemp))) 
				return (false);
      m_bMuteStatus = bTemp;
      //result = (CS_SetMute(CS_ALL, (signed char)bTemp) ? true : false);
			break;

		case adpLISTENER_POSITION:// sets listener position
			return (false);

			break;
		case adpLISTENER_VELOCITY:// sets 3d listener velocity
			return (false);

			break;
		case adpLISTENER_FORWARD:// sets 3d listener forward value
			return (false);

			break;
		case adpLISTENER_TOP:// sets 3d listener top value
			return (false);

      break;
    case adpWINDOW_HANDLE:// sets fmod window output handle
      if (!(pParam->GetValue(vpTemp))) 
				return (false);
      //result = (CS_SetHWND(vpTemp) ? true : false);
      m_pHWnd = vpTemp;
      break;
    case adpSYSTEM_FREQUENCY:// sets fmod global mixing speed
      if (!(pParam->GetValue(nTemp))) 
				return (false);
      m_nSystemFrequency = nTemp;
			//result = (CS_SetFrequency(CS_ALL, nTemp) ? true : false);
			break;
		case adpMASTER_VOLUME:// sets the fmod master volume 0 - 1, 1 being highest volume
			{
				result = false; // TODO: Remove this as soon as this method has been cleaned up.

				if (pParam->GetValue(fTemp) && m_pAPISystem && m_pEventSystem)
				{
					clamp(fTemp, 0.0f, 1.0f);

					CFrameProfilerSection const SoundFrameProfilerSection(CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_API_OTHER));

					// Set the master channel group's volume.
					FMOD::ChannelGroup* pMasterChannelGroup = NULL;
					m_ExResult = m_pAPISystem->getMasterChannelGroup(&pMasterChannelGroup);

					if (!IS_FMODERROR)
					{
						m_ExResult = pMasterChannelGroup->setVolume(fTemp);

						if (!IS_FMODERROR)
						{
							result = true;
						}
						else
						{
							FmodErrorOutput("Setting master volume on master channel group failed!", eSLT_Warning);
							result = false;
						}
					}
					else
					{
						FmodErrorOutput("Getting master channel group for setting master volume failed!", eSLT_Warning);
						result = false;
					}

					assert(result);

					// Set the master category's volume.
					FMOD::EventCategory* pMasterCategory = NULL;
					m_ExResult = m_pEventSystem->getCategory("master", &pMasterCategory);

					if (!IS_FMODERROR)
					{
						m_ExResult = pMasterCategory->setVolume(fTemp);

						if (!IS_FMODERROR)
						{
							result = true;
						}
						else
						{
							FmodErrorOutput("Setting master volume on master category failed!", eSLT_Warning);
						}
					}
					else
					{
						FmodErrorOutput("Getting master category for setting master volume failed!", eSLT_Warning);
					}

					assert(result);
				}

				break;
			}
		case adpMASTER_PAUSE: // value true/false
			{
				if (!(pParam->GetValue(bTemp))) 
					return (false);

				CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_API_OTHER) );
				FMOD::ChannelGroup *MasterChannelGroup = 0;
				m_ExResult = m_pAPISystem->getMasterChannelGroup(&MasterChannelGroup);
				
				if (IS_FMODERROR)
				{
					FmodErrorOutput("system get master channel group failed! ", eSLT_Warning);
					return false;
				}

				m_ExResult = MasterChannelGroup->setPaused(bTemp);
				if (IS_FMODERROR)
				{
					FmodErrorOutput("master channel group set pause failed! ", eSLT_Warning);
					return false;
				}

				if (m_pEventSystem)
				{
					FMOD::EventCategory *pMasterCategory = NULL;
					m_ExResult = m_pEventSystem->getCategory("master", &pMasterCategory);

					if (IS_FMODERROR)
					{
						FmodErrorOutput("get master category failed! ", eSLT_Warning);
						return false;
					}

					m_ExResult = pMasterCategory->setPaused(bTemp);
					if (IS_FMODERROR)
					{
						FmodErrorOutput("master category set pause failed! ", eSLT_Warning);
						return false;
					}
				}

				break;
			}
		case adpPITCH:
			{
				if (!(pParam->GetValue(fTemp)) || !m_pEventSystem) 
					return (false);

				if (fTemp < -4.0f)
					fTemp = -4.0f;
				else if (fTemp > 4.0f)
					fTemp = 4.0f;

				FMOD::EventCategory *pPlatformCategory = NULL;
				m_ExResult = m_pEventSystem->getCategory("master", &pPlatformCategory);

				if (IS_FMODERROR)
				{
					FmodErrorOutput("system get master category failed! ", eSLT_Warning);
					return false;
				}

				m_ExResult = pPlatformCategory->setPitch(fTemp);

				if (IS_FMODERROR)
				{
					FmodErrorOutput("master category set pitch failed! ", eSLT_Warning);
					return false;
				}

				break;
			}

		case adpSPEAKER_MODE:// sets fmod speaker enum
			
		//	typedef enum
		//	{
		//	FMOD_SPEAKERMODE_RAW,              /* There is no specific speakermode.  Sound channels are mapped in order of input to output.  See remarks for more information. */
		//	FMOD_SPEAKERMODE_MONO,             /* The speakers are monaural. */
		//	FMOD_SPEAKERMODE_STEREO,           /* The speakers are stereo (default value). */
		//	FMOD_SPEAKERMODE_4POINT1,          /* 4.1 speaker setup.  This includes front, center, left, rear and a subwoofer. Also known as a "quad" speaker configuration. */
		//	FMOD_SPEAKERMODE_5POINT1,          /* 5.1 speaker setup.  This includes front, center, left, rear left, rear right and a subwoofer. */
		//	FMOD_SPEAKERMODE_7POINT1,          /* 7.1 speaker setup.  This includes front, center, left, rear left, rear right, side left, side right and a subwoofer. */
		//	FMOD_SPEAKERMODE_PROLOGIC,         /* Stereo output, but data is encoded in a way that is picked up by a Prologic/Prologic2 decoder and split into a 5.1 speaker setup. */

		//	FMOD_SPEAKERMODE_MAX,              /* Maximum number of speaker modes supported. */
		//	FMOD_SPEAKERMODE_FORCEINT = 65536  /* Makes sure this enum is signed 32bit. */
		//} FMOD_SPEAKERMODE;
	
				//" 0: Control Panel Settings\n"
				//"	1: Mono\n"
				//"	2: Stereo\n"
				//"	3: Headphone\n"
				//"	4: 4Point1\n"
				//"	5: 5Point1\n"
				////"	6: Surround\n"
				//"	7: 7Point1\n"
				//"	9: Prologic\n"

      // ***** Setspeakermode also sets the PAN separation setting to -0- if set to mono and -1- if stereo
			if (!(pParam->GetValue(m_InitSettings.nSpeakerConfig))) 
				return (false);
			switch (m_InitSettings.nSpeakerConfig)
			{
			case 0 :
				IsEax(); // polls the state of the control panel;
				break;
			case 1 :
				m_nSpeakerMode = FMOD_SPEAKERMODE_MONO;
				break;
			case 2 :
				m_nSpeakerMode = FMOD_SPEAKERMODE_STEREO;
				break;
			case 4 :
				m_nSpeakerMode = FMOD_SPEAKERMODE_QUAD;
				break;
			case 5 :
				m_nSpeakerMode = FMOD_SPEAKERMODE_SURROUND;
				break;
			case 6 :
				m_nSpeakerMode = FMOD_SPEAKERMODE_5POINT1;
				break;
			case 8 :
				m_nSpeakerMode = FMOD_SPEAKERMODE_7POINT1;
				break;
			case 9 :
				m_nSpeakerMode = FMOD_SPEAKERMODE_SRS5_1_MATRIX;
				break;
			default:
				return false; // Unknown speaker config
			}
			//ResetAudio();
			//m_ExResult = m_pFMODEX->setSpeakerMode(m_nSpeakerMode);
      break;
    case adpPAUSED_STATUS:// pauses or un pauses system
      if (!(pParam->GetValue(bTemp))) 
				return (false);

			// TODO VERIFY IMPLEMENTATION
      m_bSystemPaused = bTemp;
      break;
    case adpSNDSYSTEM_OUTPUTTYPE:// **** OPTIONAL ***** (-1 lets fmod pick best one) sets the fmod mixer driver type 
    case adpSNDSYSTEM_DRIVER: 
      if (!(pParam->GetValue(nTemp))) 
				return (false);
      
			// TODO VERIFY IMPLEMENTATION
      // -1 tells the fmod system to pick the best one -- these are only valid choices
      if ((nTemp == -1) || (nTemp == FMOD_OUTPUTTYPE_NOSOUND) || (nTemp == FMOD_OUTPUTTYPE_WINMM) || (nTemp == FMOD_OUTPUTTYPE_DSOUND))
      break;

      case adpSTOPALL_CHANNELS:// stops all channels
				// TODO VERIFY IMPLEMENTATION
        break;

      case adpDOPPLER: // turn doppler on or off with a bool
        if (!(pParam->GetValue(bTemp))) 
					return (false);

				// TODO VERIFY IMPLEMENTATION
        m_bDopplerStatus = bTemp;
        break;

    default:
      break;
    }
  return result;
  }

	// return index of Project despite if it is available 
	ProjectIndex CAudioDeviceFmodEx400::LoadProjectFile(const char* sProjectPath, const char* sProjectName, bool bAsync)
	{
		CryAutoCriticalSection SmartCriticalSection(m_CSAD_Projects);

		MEMSTAT_CONTEXT_FMT(EMemStatContextTypes::MSC_SoundProject, 0, "%s (%s)", sProjectName, sProjectPath);

		VecProjectFilesIter IterProject = m_VecLoadedProjectFiles.end();
		SProjectFile* pProjectFile = NULL;

		if (m_pEventSystem)
		{
			string sFull = sProjectPath;
			sFull += sProjectName;
			
			VecProjectFilesIter IterEnd = m_VecLoadedProjectFiles.end();
			for (VecProjectFilesIter Iter = m_VecLoadedProjectFiles.begin(); Iter!=IterEnd; ++Iter)
			{
				if (sFull == (*Iter).sProjectName)
				{
					pProjectFile = &(*Iter);
					break;
				}
			}

			if (!pProjectFile)
			{
				// This is a new Project and was not found yet
				SProjectFile NewProject;
				NewProject.nProjectIndex				= m_VecLoadedProjectFiles.size();;
				NewProject.sProjectName					= sFull;
				NewProject.pProjectHandle				= PROJECTHANDLE_INVALID;
				NewProject.pProjectFileInMemory	= 0;
				NewProject.pReadStream					= 0;
				NewProject.nRefCount						= 0;
				NewProject.tLastTimeUsed				= gEnv->pTimer->GetFrameStartTime();
				NewProject.bPreloadWholeProject	= true;
				NewProject.nFileSize						= 0;

				m_VecLoadedProjectFiles.push_back(NewProject);

				pProjectFile = (SProjectFile*)&(m_VecLoadedProjectFiles[NewProject.nProjectIndex]);
			}
			
			if (pProjectFile)
			{
				if (pProjectFile->pProjectHandle != PROJECTHANDLE_INVALID || pProjectFile->pReadStream)
					return pProjectFile->nProjectIndex;

				// deactive SetMediaPath as long as FMOD can not track individual
				// FEV Paths internaly if the file is loaded from memory
				//m_ExResult = m_pEventSystem->setMediaPath((char*)sProjectPath);
				//if (IS_FMODERROR)
				//{
				//	FmodErrorOutput("set event media path failed! ", eSLT_Warning);
				//	return NULL;
				//}

				//{
				//{
				//	if (m_pSoundSystem->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
				//		m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::EVENTSYSTEM_SETMEDIAPATH, (int)gEnv->pTimer->GetFrameStartTime().GetMilliSeconds(), (void*) sProjectPath.c_str());

				//	CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_API_OTHER) );
				//	m_ExResult = m_pEventSystem->setMediaPath((char*)sProjectPath);
				//}
				//if (IS_FMODERROR)
				//{
				//	FmodErrorOutput("set event media path failed! ", eSLT_Warning);
				//	return NULL;
				//}

				TFixedResourceName ProjectPathAndName = sProjectPath;
				ProjectPathAndName += sProjectName;

				uint32 nSize = gEnv->pCryPak->FGetSize(ProjectPathAndName.c_str());

				if (!pProjectFile->pProjectFileInMemory && nSize)
				{
					pProjectFile->pProjectFileInMemory = new int8[ nSize ];
				}

				if (!pProjectFile->pProjectFileInMemory)
					return pProjectFile->nProjectIndex;

				StreamReadParams StrParams;
				StrParams.nFlags = 0;



				StrParams.dwUserData = pProjectFile->nProjectIndex;

				StrParams.nLoadTime = 0;
				StrParams.nMaxLoadTime = 0;
				StrParams.ePriority = estpUrgent;
				StrParams.pBuffer = pProjectFile->pProjectFileInMemory;
				StrParams.nOffset = 0;
				StrParams.nSize = nSize;

#ifdef FMOD_STREAMING_DEBUGGING
				{
					CryAutoCriticalSection SmartCriticalSectionDebug(m_CSDebug);
					++g_nStreamingDebugCount;
#ifdef FMOD_STREAMING_DEBUG_LOGGING
					gEnv->pSoundSystem->Log(eSLT_Always, "AD_LOAD_START: %s (%d)", ProjectPathAndName.c_str(), g_nStreamingDebugCount);
#endif // FMOD_STREAMING_DEBUG_LOGGING
				}
#endif // FMOD_STREAMING_DEBUGGING

				pProjectFile->pReadStream = m_pSoundSystem->m_pStreamEngine->StartRead(eStreamTaskTypeSound, ProjectPathAndName.c_str(), this, &StrParams);

				if (!bAsync)
					pProjectFile->pReadStream->Wait();

				return pProjectFile->nProjectIndex;
			}
		}

		return PROJECTINDEX_INVALID;
	}

	//////////////////////////////////////////////////////////////////////////
	void CAudioDeviceFmodEx400::StreamOnComplete(IReadStream *pStream, unsigned nError)
	{
		FUNCTION_PROFILER( gEnv->pSystem,PROFILE_SOUND );

		CryAutoCriticalSection SmartCriticalSection(m_CSAD_Projects);

		unsigned int nProjectIndex = (unsigned int) pStream->GetUserData();

		if (nProjectIndex < m_VecLoadedProjectFiles.size())
		{
			SProjectFile& project = m_VecLoadedProjectFiles[nProjectIndex];

			assert (project.nProjectIndex == nProjectIndex);

			project.pReadStream = NULL;
			project.nFileSize = 0;

			if (nError)
			{
				if (nError != ERROR_USER_ABORT)
				{
					FmodErrorOutput("load event project failed! " + project.sProjectName, eSLT_Error);
					assert(!"FMOD Project not found!");
				}

				// Remove the data
				if (project.pProjectFileInMemory)
				{
					delete[] project.pProjectFileInMemory;
					project.pProjectFileInMemory = NULL;
					project.pProjectHandle = PROJECTHANDLE_INVALID;
				}
			}
			else
			{
#ifdef FMOD_STREAMING_DEBUGGING
				{
					CryAutoCriticalSection SmartCriticalSectionDebug(m_CSDebug);
					--g_nStreamingDebugCount;
#ifdef FMOD_STREAMING_DEBUG_LOGGING
					gEnv->pSoundSystem->Log(eSLT_Always, "AD_SOC: %s (%d)", project.sProjectName.c_str(), g_nStreamingDebugCount);
#endif // FMOD_STREAMING_DEBUG_LOGGING
				}
#endif // FMOD_STREAMING_DEBUGGING

				project.nFileSize = pStream->GetBytesRead();
				m_bNewProject = true;
			}
		}
	}

	// releases the data of a project but keeps it known to the system 
	bool CAudioDeviceFmodEx400::UnloadProjectFile(const ProjectIndex nProjectIndex)
	{
		// go through Projects and unload those which are not referenced
		CryAutoCriticalSection SmartCriticalSection(m_CSAD_Projects);

		if (static_cast<size_t>(nProjectIndex) < m_VecLoadedProjectFiles.size())
		{
			SProjectFile& project = m_VecLoadedProjectFiles[nProjectIndex];

			assert (project.nProjectIndex == nProjectIndex);

			// free preloaded FSBs first
			project.PreloadedFSBs.clear();

			if (project.pProjectHandle != PROJECTHANDLE_INVALID)
			{
				FMOD::EventProject* const pDeleteProject	= (FMOD::EventProject*)project.pProjectHandle;
				stack_string const sProjectName						= project.sProjectName;

				m_ExResult	= pDeleteProject->cancelAllLoads();
				assert(m_ExResult == FMOD_OK);

				// We're flushing all sound and music related streams to finish their tasks. Not executed streaming requests are cancelled before.
				// We need this for proper project release, because fmod waits to finish all streaming requests and it never happens for synchronous callbacks.
				gEnv->pSystem->GetStreamEngine()->Update(STREAM_TASK_TYPE_AUDIO_ALL);
				m_ExResult	= pDeleteProject->release();
				assert(m_ExResult == FMOD_OK);

				if (project.pReadStream)
				{
					project.pReadStream->Abort(); // TODO: Check this as it seems potentially dangerous as pReadStream turns into a dangling pointer after Abort()!
					project.pReadStream = NULL;
				}

				project.pProjectHandle	= PROJECTHANDLE_INVALID;
				project.nFileSize				= 0;

				if (m_pSoundSystem->g_nDebugSound == SOUNDSYSTEM_DEBUG_SIMPLE)
				{
					if (m_ExResult == FMOD_OK)
						m_pSoundSystem->Log(eSLT_Message, "<Sound> AD: Project released: %s", sProjectName.c_str());
					else
						m_pSoundSystem->Log(eSLT_Error, "<Sound> AD: Project release failed: %s", sProjectName.c_str());
				}
			}

			assert( project.pReadStream == NULL );
			if (project.pReadStream)
			{
				project.pReadStream->Abort();
				project.pReadStream = NULL;
			}

			SAFE_DELETE_ARRAY(project.pProjectFileInMemory);
			
			return (m_ExResult == FMOD_OK);
		}

		return false;
	}


	// retrieves the project entry by the project index 
	SProjectFile* CAudioDeviceFmodEx400::GetProjectFile(const ProjectIndex nProjectIndex, const bool bUpdateTimer)
	{
		SProjectFile* project = NULL;
		CryAutoCriticalSection SmartCriticalSection(m_CSAD_Projects);

		if (static_cast<size_t>(nProjectIndex) < m_VecLoadedProjectFiles.size())
		{
			project = &m_VecLoadedProjectFiles[nProjectIndex];

			assert (project->nProjectIndex == nProjectIndex);

			// renew the time
			if (bUpdateTimer)
				project->tLastTimeUsed = gEnv->pTimer->GetFrameStartTime();
		}

		return project;
	}

	void CAudioDeviceFmodEx400::UpdateGroupEventCount()
	{
		m_nCountProject = 0;
		m_nCountGroup = 0;
		m_nCountEvent = 0;
		int nProjectIndex = 0;
		int nGroupIndex = 0;
		m_ExResult = FMOD_OK;
		FMOD::EventProject* pProject = NULL;
		FMOD::EventGroup*		pGroup = NULL;

		TFixedResourceName sReferenceName;

		if (m_pSoundSystem->g_nDumpEventStructure)
		{
			if (m_pEventDumpLogger)
				delete m_pEventDumpLogger;

			m_pEventDumpLogger = (CDebugLogger*) new CDebugLogger("EventStructure.txt");
		}

		while(m_ExResult == FMOD_OK)
		{
			{
				CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_API_OTHER) );
				m_ExResult = m_pEventSystem->getProjectByIndex(nProjectIndex, &pProject);
			}
			if (IS_FMODERROR)
			{
				//FmodErrorOutput("event system get project by index failed! ", eSLT_Warning);
			}

			if (pProject && (m_ExResult == FMOD_OK))
			{
				if (m_pSoundSystem->g_nDumpEventStructure)
				{
					sReferenceName = "Sounds/";
					FMOD_EVENT_PROJECTINFO oProjectInfo;
					m_ExResult = pProject->getInfo(&oProjectInfo);
					sReferenceName += oProjectInfo.name;
					sReferenceName += ":";
				}
				
				while(m_ExResult == FMOD_OK)
				{
					{
						CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_API_OTHER) );
						m_ExResult = pProject->getGroupByIndex(nGroupIndex, false, &pGroup);
					}

					if (IS_FMODERROR)
					{
						//FmodErrorOutput("project get group by index failed! ", eSLT_Warning);
					}

					if (pGroup && (m_ExResult == FMOD_OK))
					{
						++nGroupIndex;
						++m_nCountGroup;
						GroupEventCount(pGroup, pProject, sReferenceName);
					}
				}
				nGroupIndex = 0;
				m_ExResult = FMOD_OK;
				++nProjectIndex;
				++m_nCountProject;
			}
		}
	}

	void CAudioDeviceFmodEx400::GroupEventCount(FMOD::EventGroup* pParentGroup, FMOD::EventProject* pProject, TFixedResourceName sReferenceName)
	{
		int nGroupIndex = 0;
		m_ExResult = FMOD_OK;
		FMOD::EventGroup* pGroup = NULL;
		FMOD::Event* pEvent = NULL;

		TFixedResourceName sLocalReferenceName = sReferenceName;

		if (m_pSoundSystem->g_nDumpEventStructure)
		{
			char *sGroupName = 0; 
			m_ExResult = pParentGroup->getInfo(0, &sGroupName);
			sLocalReferenceName += sGroupName;
		}

		// counting Groups
		while(m_ExResult == FMOD_OK)
		{
			{
				CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_API_OTHER) );
				m_ExResult = pParentGroup->getGroupByIndex(nGroupIndex, false, &pGroup);
			}
			if (IS_FMODERROR)
			{
				//FmodErrorOutput("group get group by index failed! ", eSLT_Warning);
			}

			if (pGroup && (m_ExResult == FMOD_OK))
			{
				TFixedResourceName sLocalReferenceNameSubGroup;

				if (m_pSoundSystem->g_nDumpEventStructure)
				{
					sLocalReferenceNameSubGroup = sLocalReferenceName;
					sLocalReferenceNameSubGroup += "/";
				}
				++nGroupIndex;
				++m_nCountGroup;
				GroupEventCount(pGroup, pProject, sLocalReferenceNameSubGroup);
			}
		}

		// counting Events
		int nNumEvents = 0;
		{
			CFrameProfilerSection SoundFrameProfilerSection( CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection_API_OTHER) );
			m_ExResult = pParentGroup->getNumEvents( &nNumEvents);
		}

		if (IS_FMODERROR)
		{
			//FmodErrorOutput("group get num. events failed! ", eSLT_Warning);
		}

		if (m_pSoundSystem->g_nDumpEventStructure)
		{
			TFixedResourceName sLocalReferenceNameEvent;

			for (int i=0; i<nNumEvents; ++i)
			{
				m_ExResult = pParentGroup->getEventByIndex(i, FMOD_EVENT_INFOONLY, &pEvent);
				
				if (IS_FMODERROR)
				{
					FmodErrorOutput("group get event by index failed! ", eSLT_Warning);
				}

				if (pEvent)
				{
					char *sEventName = 0;
					m_ExResult = pEvent->getInfo(0, &sEventName, 0);

					if (m_ExResult == FMOD_OK && m_pEventDumpLogger)
					{
						sLocalReferenceNameEvent = sLocalReferenceName;
						sLocalReferenceNameEvent += ":";
						sLocalReferenceNameEvent += sEventName;
						m_pEventDumpLogger->LogString(sLocalReferenceNameEvent.c_str());
					}


				}

			}
			
		}

		if (!IS_FMODERROR)
			m_nCountEvent += nNumEvents;

	}

	// writes output to screen in debug
	void CAudioDeviceFmodEx400::DrawInformation(IRenderer* pRenderer, float xpos, float ypos, int nSoundInfo)
	{
#ifdef INCLUDE_AUDIO_PRODUCTION_CODE
		float fColor[4]				= {1.0f, 1.0f, 1.0f, 0.7f};
		float fWhite[4]				= {1.0f, 1.0f, 1.0f, 0.7f};
		float fBlue[4]				= {0.0f, 0.0f, 1.0f, 0.7f};
		float fCyan[4]				= {0.0f, 1.0f, 1.0f, 0.7f};
		float fColorOrange[4]	= {1.0f, 0.5f, 0.0f, 0.7f};
		float fGreen[4]				= {0.0f, 0.8f, 0.0f, 1.0f};
		float fYellow[4]			= {1.0f, 1.0f, 0.0f, 0.7f};



































		// Info that is always to be drawn regardless of nSoundInfo
		pRenderer->Draw2dLabel(xpos, ypos, 1.35f, fColor, false, "FMOD Version: %s FMOD Event Version: %s", m_sFMODSystemVersion.c_str(), m_sFMODEventSystemVersion.c_str());

		switch (nSoundInfo)
		{
		case 1:
			{
				xpos							= 600.0f;
				ypos							= 120.0f;
				uint32 nFileSeeks	= 0;
				uint32 nFileReads	= 0;

#ifdef FMOD_STREAMING_DEBUGGING
				nFileSeeks = s_nFMODFileSeekCount;
				nFileReads = s_nFMODFileReadCount;
				CTimeValue const tTimeDif = gEnv->pTimer->GetFrameStartTime() - m_tLastBandwidthTime;
				float const fSecs	= tTimeDif.GetSeconds();

				if (abs(fSecs) > 1.0f)
				{
					m_fStreamKBPerSecond = (s_nFMODFileReadBandwidth / fSecs) / 1024.0f;
					s_nFMODFileReadBandwidth = 0;
					m_tLastBandwidthTime = gEnv->pTimer->GetFrameStartTime();
				}
#endif // FMOD_STREAMING_DEBUGGING

				pRenderer->Draw2dLabel(xpos, ypos, 1.5, fColor, false, "Channel sounds: %d, Event sounds: %d", (int)m_UnusedPlatformSounds.size(), (int)m_UnusedPlatformSoundEvents.size() );
				ypos += 12;
				pRenderer->Draw2dLabel(xpos, ypos, 1.5, fColor, false, "Open file handles: %d, Total reads: %d", s_nFileOpenCount, (int)nFileReads);
				ypos += 12;

#ifdef FMOD_STREAMING_DEBUGGING
				pRenderer->Draw2dLabel(xpos, ypos, 1.5, fColor, false, "Stream Bandwidth %.1f KBs/sec, File Seeks: %d", m_fStreamKBPerSecond, (int)nFileSeeks );
				ypos += 12;
#endif // FMOD_STREAMING_DEBUGGING

				if (m_pEventSystem)
				{
					int nNum = 64;
					FMOD_EVENT_SYSTEMINFO SystemInfo;
					memset(&SystemInfo, 0, sizeof(FMOD_EVENT_SYSTEMINFO));

					SystemInfo.numplayingevents = nNum;
					FMOD_EVENT* PlayingEvents[64];
					SystemInfo.playingevents = PlayingEvents;

					m_ExResult = m_pEventSystem->getInfo( &SystemInfo );
					if (IS_FMODERROR)
					{
						FmodErrorOutput("event system get system info failed! ", eSLT_Warning);
						return;
					}

					pRenderer->Draw2dLabel(xpos, ypos, 1, fColor, false, "----- Event Names %d -----", SystemInfo.numplayingevents );
					//pRenderer->Draw2dLabel(xpos, ypos, 1.5, fColor, false, "Playing Events: %d", );
					ypos += 10;

					for (int i=0; i<SystemInfo.numplayingevents; ++i)
					{
						char *sName; 
						FMOD::Event* pEvent = (FMOD::Event*)SystemInfo.playingevents[i];
						FMOD_EVENT_INFO EventInfo;
						FMOD_EVENT_WAVEBANKINFO WavebankInfo = {{0},0};
						EventInfo.maxwavebanks = 1;
						EventInfo.wavebankinfo = &WavebankInfo;
						float fVolume = 0.0f;

						m_ExResult = pEvent->getInfo(0, &sName, &EventInfo);
						m_ExResult = pEvent->getVolume(&fVolume);

						if (IS_FMODERROR)
						{
							FmodErrorOutput("event get info failed! ", eSLT_Warning);
						}
						else
						{
							switch (WavebankInfo.type)
							{
							case 0: // Events that are streamed from disk
								{
									pRenderer->Draw2dLabel(xpos, ypos, 1.2f, (fVolume > 0.0f) ? fColorOrange : fCyan, false, "%s in %s", sName, WavebankInfo.name);
									break;
								}
							case 1: // Events that are loaded into memory
								{
									pRenderer->Draw2dLabel(xpos, ypos, 1.2f, fColor, false, "%s in %s", sName, WavebankInfo.name);
									break;
								}
							}

							ypos += 10;
						}
					}
				}
			}
			break;
		case 7:
			{
				uint32 nUsedMem = 0;

				IWavebank *pWavebank = NULL;

				ypos = 120.0f;

				tmapWavebanks::iterator ItEnd = m_Wavebanks.end();
				for (tmapWavebanks::const_iterator It = m_Wavebanks.begin(); It!=ItEnd; ++It)
				{
					pWavebank = (*It).second;
					nUsedMem += pWavebank->GetInfo()->nMemCurrentlyInByte;
					if (pWavebank->GetInfo()->nMemCurrentlyInByte > 0)
					{
						pRenderer->Draw2dLabel(xpos, ypos, 1.5, fColor, false, "%s Mem: %dKB File: %dKB", pWavebank->GetName(), pWavebank->GetInfo()->nMemCurrentlyInByte/1024, pWavebank->GetInfo()->nFileSize/1024);
						ypos += 10;
					}
				}

				ypos += 10;
				pRenderer->Draw2dLabel(xpos, ypos, 1.5, fColor, false, "Total Wavebank Memory: %.1f MB", (nUsedMem/1024)/1024.0f);
				ypos += 10;
			}
			break;
		case 11:
		case 12:
		case 13:
			{
				xpos = 10.0f;
				ypos = 90.0f;
				stack_string sText;
				FMOD::EventProject* pEventProject = NULL;
				FMOD_EVENT_PROJECTINFO oEventProjectInfo;
				FMOD_MEMORY_USAGE_DETAILS oMemoryUSageDetails = {0};
				int nEventProjectIndex = -1;
				int unsigned nMemoryTotal	= 0;
				XmlNodeRef oRoot, oNodeProject, oNodeGroup, oNodeEvent;
				bool const bLogToFile = (_stricmp(m_pSoundSystem->g_sSoundInfoLogFile->GetString(), "") != 0);
				
				m_pEventSystem->getMemoryInfo(FMOD_MEMBITS_ALL, FMOD_EVENT_MEMBITS_ALL, &nMemoryTotal, &oMemoryUSageDetails);
				int nNumProjects = 0;
				m_pEventSystem->getNumProjects(&nNumProjects);
				sText.Format("EventSystem (%d byte)\nProjects: %d", nMemoryTotal, nNumProjects);
				pRenderer->Draw2dLabel(xpos, ypos, 1.6f, fColorOrange, false, "%s", sText.c_str());

				// Also draw FileCacheManager info
				CFileCacheManager* const pFileCacheManager = m_pSoundSystem->GetFileCacheManager();

				if (pFileCacheManager)
					pFileCacheManager->DrawInformation(pRenderer, 250.0f, ypos, nSoundInfo);

				// Check if we're supposed to create a log file
				if (bLogToFile)
				{
					oRoot = gEnv->pSystem->CreateXmlNode("Level");
					ILevel* const pCurrentLevel = gEnv->pGame->GetIGameFramework()->GetILevelSystem()->GetCurrentLevel();
					oRoot->setAttr("Name", pCurrentLevel ? pCurrentLevel->GetLevelInfo()->GetName() : "NoLevelLoaded");
					oRoot->setAttr("Memory", nMemoryTotal);
				}

				xpos += 10.0f;
				ypos += 30.0f;

				while (m_pEventSystem->getProjectByIndex(++nEventProjectIndex, &pEventProject) == FMOD_OK)
				{
					ypos += 10.0f;
					memset(&oEventProjectInfo, 0, sizeof(FMOD_EVENT_PROJECTINFO));
					pEventProject->getInfo(&oEventProjectInfo);
					pEventProject->getMemoryInfo(FMOD_MEMBITS_ALL, FMOD_EVENT_MEMBITS_ALL, &nMemoryTotal, &oMemoryUSageDetails);

					sText.Format("%s (%d byte)", oEventProjectInfo.name, nMemoryTotal);

					pRenderer->Draw2dLabel(xpos, ypos, 1.2f, fCyan, false, "%s", sText.c_str());
					
					// Log project data
					if (bLogToFile && oRoot)
					{
						oNodeProject = oRoot->newChild("Project");
						oNodeProject->setAttr("Name", oEventProjectInfo.name);
						oNodeProject->setAttr("Memory", nMemoryTotal);
					}

					if (nSoundInfo == 12 || nSoundInfo == 13)
					{
						// Traverse over all this project's groups and their sub-groups
						FMOD::EventGroup* pEventGroup			= NULL;
						FMOD::EventGroup* pTempEventGroup	= NULL;
						int nEventGroupIndex							= -1;
						
						xpos += 10.0f;

						while (pEventProject->getGroupByIndex(++nEventGroupIndex, false, &pEventGroup) == FMOD_OK)
						{
							ypos += 10.0f;

							char* pcName = NULL;
							pEventGroup->getInfo(NULL, &pcName);
							pEventGroup->getMemoryInfo(FMOD_MEMBITS_ALL, FMOD_EVENT_MEMBITS_ALL, &nMemoryTotal, &oMemoryUSageDetails);
							sText.Format("%s (%d byte)", pcName, nMemoryTotal);

							pRenderer->Draw2dLabel(xpos, ypos, 1.2f, fYellow, false, "%s", sText.c_str());

							// Also log all groups of this project
							if (bLogToFile)
							{
								oNodeGroup = oNodeProject->newChild("Group");
								oNodeGroup->setAttr("Name", pcName);
								oNodeGroup->setAttr("Memory", nMemoryTotal);
							}

							// Draw this group's events
							if (nSoundInfo == 13)
							{
								xpos += 10.0f;
								int nIndex = -1;
								FMOD::Event* pEvent = NULL;
								while (pEventGroup->getEventByIndex(++nIndex, FMOD_EVENT_INFOONLY, &pEvent) == FMOD_OK)
								{
									ypos += 10.0f;

									pEvent->getInfo(NULL, &pcName, NULL);
									pEvent->getMemoryInfo(FMOD_MEMBITS_ALL, FMOD_EVENT_MEMBITS_ALL, &nMemoryTotal, &oMemoryUSageDetails);
									sText.Format("%s (%d byte)", pcName, nMemoryTotal);

									pRenderer->Draw2dLabel(xpos, ypos, 1.2f, fWhite, false, "%s", sText.c_str());

									// Also log all events of this group
									if (bLogToFile)
									{
										oNodeEvent = oNodeGroup->newChild("Event");
										oNodeEvent->setAttr("Name", pcName);
										oNodeEvent->setAttr("Memory", nMemoryTotal);
									}
								}

								xpos -= 10.0f;
							}

							// Traverse over the sub-groups if there are any
							pEventGroup->getGroupByIndex(0, false, &pEventGroup);

							xpos += 10.0f;

							while (pEventGroup)
							{
								ypos += 10.0f;
								pEventGroup->getInfo(NULL, &pcName);
								pEventGroup->getMemoryInfo(FMOD_MEMBITS_ALL, FMOD_EVENT_MEMBITS_ALL, &nMemoryTotal, &oMemoryUSageDetails);
								sText.Format("%s (%d byte)", pcName, nMemoryTotal);

								pRenderer->Draw2dLabel(xpos, ypos, 1.2f, fGreen, false, "%s", sText.c_str());

								// Also log all sub-groups of this group
								XmlNodeRef oNodeSubGroup;
								if (bLogToFile)
								{
									oNodeSubGroup = oNodeGroup->newChild("Group");
									oNodeSubGroup->setAttr("Name", pcName);
									oNodeSubGroup->setAttr("Memory", nMemoryTotal);
								}

								// Draw this sub-group's events
								if (nSoundInfo == 13)
								{
									xpos += 10.0f;
									int nIndex = -1;
									FMOD::Event* pEvent = NULL;
									while (pEventGroup->getEventByIndex(++nIndex, FMOD_EVENT_INFOONLY, &pEvent) == FMOD_OK)
									{
										ypos += 10.0f;

										pEvent->getInfo(NULL, &pcName, NULL);
										pEvent->getMemoryInfo(FMOD_MEMBITS_ALL, FMOD_EVENT_MEMBITS_ALL, &nMemoryTotal, &oMemoryUSageDetails);
										sText.Format("%s (%d byte)", pcName, nMemoryTotal);

										pRenderer->Draw2dLabel(xpos, ypos, 1.2f, fWhite, false, "%s", sText.c_str());

										// Also log all events of this sub-group
										if (bLogToFile)
										{
											oNodeEvent = oNodeSubGroup->newChild("Event");
											oNodeEvent->setAttr("Name", pcName);
											oNodeEvent->setAttr("Memory", nMemoryTotal);
										}
									}

									xpos -= 10.0f;
								}

								// Is there another sub-group?
								pEventGroup->getGroupByIndex(0, false, &pTempEventGroup);
								
								if (pTempEventGroup)
								{
									pEventGroup = pTempEventGroup;
									xpos += 10.0f;
								}
								else
								{
									// Check if there's a next group on this level
									int nIndex = -1;
									pEventGroup->getInfo(&nIndex, NULL);
									pEventGroup->getParentGroup(&pTempEventGroup);
									pTempEventGroup->getGroupByIndex(nIndex+1, false, &pEventGroup);
									
									while (!pEventGroup)
									{
										pTempEventGroup->getInfo(&nIndex, NULL);
										pTempEventGroup->getParentGroup(&pTempEventGroup);

										if (pTempEventGroup)
										{
											pTempEventGroup->getGroupByIndex(nIndex+1, false, &pEventGroup);
											xpos -= 10.0f;
										}
										else
											break; // We are at top level
									}
								}
							}

							xpos -= 10.0f;
						}

						xpos -= 10.0f;
					}
				}

				// Save the to the log file and reset the log file cvar
				if (bLogToFile && oRoot)
				{
					stack_string sPathAndFile;
					sPathAndFile.Format("./%s.xml", m_pSoundSystem->g_sSoundInfoLogFile->GetString());
					oRoot->saveToFile(sPathAndFile.c_str());


























































					m_pSoundSystem->g_sSoundInfoLogFile->Set("");
				}

				break;
			}
		}
#endif // INCLUDE_AUDIO_PRODUCTION_CODE
	}


	// retrieves the bandwidth calculations for the audio streaming
	void CAudioDeviceFmodEx400::GetBandwidthStats(float * fBandwidthRequested)
	{
#ifdef FMOD_STREAMING_DEBUGGING
		if ( fBandwidthRequested )
		{
			CTimeValue const tTimeDif = gEnv->pTimer->GetFrameStartTime() - m_tLastBandwidthTime;
			float const fSecs = tTimeDif.GetSeconds();

			if (abs(fSecs) > 1.0f)
			{
				m_fStreamKBPerSecond = (s_nFMODFileReadBandwidth / fSecs) / 1024.0f;
				s_nFMODFileReadBandwidth = 0;
				m_tLastBandwidthTime = gEnv->pTimer->GetFrameStartTime();
			}
				
			*fBandwidthRequested = m_fStreamKBPerSecond;
		}
#endif // FMOD_STREAMING_DEBUGGING
	}

	CFrameProfiler* CAudioDeviceFmodEx400::GetFMODFrameProfiler(adfpFMODProfileSection enumProfileSection)
	{
		switch (enumProfileSection)
		{
		case adfpFMODProfileSection_API_INFO:
			return s_pFrameProfiler_API_Info;
			break;
		case adfpFMODProfileSection_API_OTHER:
			return s_pFrameProfiler_API_Other;
			break;
		case adfpFMODProfileSection_BUFFER_OTHER:
			return s_pFrameProfiler_Buffer_Other;
			break;
		case adfpFMODProfileSection_BUFFER_RELEASE:
			return s_pFrameProfiler_Buffer_Release;
			break;
		case adfpFMODProfileSection_EVENT_OTHER:
			return s_pFrameProfiler_Event_Other;
			break;
		case adfpFMODProfileSection_GET_PARAM_BY_TYPE:
			return s_pFrameProfiler_Get_Param_By_Type;
			break;
		case adfpFMODProfileSection_SET_PARAM_BY_TYPE:
			return s_pFrameProfiler_Set_Param_By_Type;
			break;
		case adfpFMODProfileSection_CREATE_SOUND:
			return s_pFrameProfiler_Create_Sound;
			break;
		case adfpFMODProfileSection_START_SOUND:
			return s_pFrameProfiler_Start_Sound;
			break;
		case adfpFMODProfileSection_STOP_SOUND:
			return s_pFrameProfiler_Stop_Sound;
			break;
		}

		return 0;
	}


	// hack for looping weapon sound MP bug
	void CAudioDeviceFmodEx400::FindLostEvent()
	{
		if (m_pEventSystem)
		{
			int nNum = 64;
			FMOD_EVENT_SYSTEMINFO SystemInfo;
			//memset(&SystemInfo, 0, sizeof(FMOD_EVENT_SYSTEMINFO));

			SystemInfo.numplayingevents = nNum;
			FMOD_EVENT* PlayingEvents[64];
			SystemInfo.playingevents = PlayingEvents;

			m_ExResult = m_pEventSystem->getInfo( &SystemInfo );
			if (IS_FMODERROR)
			{
				FmodErrorOutput("event system get system info failed! ", eSLT_Warning);
				return;
			}

			for (int i=0; i<SystemInfo.numplayingevents; ++i)
			{
				char *sName; 
				FMOD::Event* pEvent = (FMOD::Event*)SystemInfo.playingevents[i];
				FMOD_EVENT_INFO EventInfo;
				//memset(&EventInfo, 0, sizeof(FMOD_EVENT_INFO));

				m_ExResult = pEvent->getInfo(0, &sName, &EventInfo);

				if (IS_FMODERROR)
				{
					FmodErrorOutput("event get info failed! ", eSLT_Warning);
				}
				else
				{
					// oneshot sounds will end eventually, 
					//so only check for looping sounds which are not controlled by the sound system
					bool bLost = (EventInfo.lengthms == -1) && !(m_pSoundSystem->IsEventUsedInPlatformSound((tSoundHandle)pEvent));
					
					if (bLost)
					{
						//m_pSoundSystem->Log("[Warning] <Sound> Lost event %s found and stopped. \n", sName );
						//m_ExResult = pEvent->stop(false);

						bool bPlaying = false;

						FMOD_EVENT_STATE EventState;
						m_ExResult = pEvent->getState(&EventState);
						if (EventState & FMOD_EVENT_STATE_PLAYING)
							bPlaying = true;
						(void)bPlaying;

						int catchme = 1;
						(void)catchme;
					}
				}

			}
		}

	}

	int CAudioDeviceFmodEx400::GetNumOpenFileHandle()
	{
		return s_nFileOpenCount;
	}

	//////////////////////////////////////////////////////////////////////////
	void CAudioDeviceFmodEx400::CreateVersionString(CryFixedStringT<16>& rStringOut) const
	{
		// Remove the leading zeros on the upper 16 bit and inject the 2 dots between the 3 groups
		size_t const nLength = rStringOut.size();
		for (size_t i = 0; i < nLength; ++i)
		{
			if (rStringOut.c_str()[0] == '0')
				rStringOut.erase(0, 1);
			else
			{
				if (i < 4)
				{
					rStringOut.insert(4-i, '.'); // First dot
					rStringOut.insert(7-i, '.'); // Second dot
					break;
				}
				else
				{
					// This shouldn't happen therefore clear the string and back out
					rStringOut.clear();
					return;
				}
			}
		}
	}

	//////////////////////////////////////////////////////////////////////////
	void CAudioDeviceFmodEx400::UpdateStreamBufferSize()
	{
		int nStreamBufferSize = m_pSoundSystem->g_nStreamBufferSize;









		// Set the internal buffer size for streams if it changed
		if (static_cast<int unsigned>(nStreamBufferSize) != m_nStreamBufferSize)
		{
			if (nStreamBufferSize > 0)
			{
				if (nStreamBufferSize < 8192)
					FmodErrorOutput("Streaming buffer size is smaller than 8192 bytes! This can lead to stuttering audio!", eSLT_Warning);

				m_ExResult = m_pAPISystem->setStreamBufferSize(nStreamBufferSize, FMOD_TIMEUNIT_RAWBYTES);
			}
			else
			{
				FmodErrorOutput("Negative values are not allowed!", eSLT_Error);
				m_ExResult = FMOD_ERR_INVALID_PARAM;
			}

			if (m_ExResult == FMOD_OK)
			{
				m_nStreamBufferSize = static_cast<int unsigned>(nStreamBufferSize);
			}
			else
			{
				int unsigned nStreamBufferSizeFmod = 0;
				m_pAPISystem->getStreamBufferSize(&nStreamBufferSizeFmod, NULL);

				stack_string sErrorText;
				sErrorText.Format("Setting of streaming buffer size failed! Attempted to set %d and is now %d!", nStreamBufferSize, nStreamBufferSizeFmod);

				m_nStreamBufferSize	= nStreamBufferSizeFmod;

				FmodErrorOutput(sErrorText.c_str(), eSLT_Error);
			}
		}
	}

	//////////////////////////////////////////////////////////////////////////
	size_t const CAudioDeviceFmodEx400::GetLeastUsedLoadingThreadID() const
	{
		if (m_pEventSystem && m_pnNumLoadsQueued)
		{
			memset(m_pnNumLoadsQueued, 0, static_cast<int>(sizeof(m_pnNumLoadsQueued)) * g_SoundCVars.g_nNumLoadingThreadsToUse);
			size_t nMinNumLoadsQueued    = std::numeric_limits<size_t>::max();
			size_t nLeastBusiestThreadID = nMinNumLoadsQueued;

			FMOD_EVENT_SYSTEMINFO oInfo;
			m_pEventSystem->getInfo(&oInfo);

			for (size_t nThreadID = 0; nThreadID < (size_t)g_SoundCVars.g_nNumLoadingThreadsToUse; ++nThreadID)
			{
				m_pnNumLoadsQueued[nThreadID] = (size_t)oInfo.numloadsqueued[nThreadID];

				// Look for an idle thread first
				if (m_pnNumLoadsQueued[nThreadID] == 0)
				{
					return nThreadID;
				}

				// Otherwise keep track of the least busiest loading thread
				if (m_pnNumLoadsQueued[nThreadID] < nMinNumLoadsQueued)
				{
					nMinNumLoadsQueued    = m_pnNumLoadsQueued[nThreadID];
					nLeastBusiestThreadID = nThreadID;
				}
			}

			return nLeastBusiestThreadID;
		}

		return static_cast<size_t>(g_SoundCVars.g_nNumLoadingThreadsToUse); // This indicates an error
	}

	//////////////////////////////////////////////////////////////////////////
	void CAudioDeviceFmodEx400::OnCVarChanged(ICVar* const pCVar)
	{
		stack_string const sTemp(pCVar->GetName());

		if (sTemp.compareNoCase("s_NumLoadingThreadsToUse") == 0)
		{
			delete[] m_pnNumLoadsQueued;
			int const nNumLoadingThreadsToUse = clamp_tpl(pCVar->GetIVal(), 1, FMOD_MAX_NUM_NONBLOCKING_LOADING_THREADS);
			m_pnNumLoadsQueued                = new size_t[nNumLoadingThreadsToUse];
			memset(m_pnNumLoadsQueued, 0, static_cast<int>(sizeof(m_pnNumLoadsQueued)) * nNumLoadingThreadsToUse);

			assert(nNumLoadingThreadsToUse == g_SoundCVars.g_nNumLoadingThreadsToUse); // In case a wrong min/max value has been set
			
			// Change callback will not get fired again
			g_SoundCVars.g_nNumLoadingThreadsToUse = nNumLoadingThreadsToUse;
		}
	}


	//////////////////////////////////////////////////////////////////////////
	bool CAudioDeviceFmodEx400::SetDefaultDriver(bool const bDeviceListChanged)
	{
		bool bSetDriver = true;

		if (bDeviceListChanged)
		{
			gEnv->pSoundSystem->Log(eSLT_Message, "<Sound> Sound-FmodEx-AudioDevice Device List changed!");





		}
		
		int nTemp = 0;
		m_ExResult = m_pAPISystem->getNumDrivers(&nTemp);

		if (IS_FMODERROR)
		{
			FmodErrorOutput("get number of drivers failed!", eSLT_Error);

#if !defined(PS3) && !defined(XENON) && !defined(CAFE)
			// enforce DSound
			m_ExResult = m_pAPISystem->setOutput(FMOD_OUTPUTTYPE_DSOUND);

			if (!IS_FMODERROR)
			{
				m_ExResult = m_pAPISystem->getNumDrivers(&nTemp);

				if (IS_FMODERROR)
				{
					FmodErrorOutput("get number of drivers failed again! ", eSLT_Error);

					return false;
				}
			}
			else
			{
				FmodErrorOutput("set sound output (enforce DSound) failed! ", eSLT_Error);

				return false;
			}


#endif
		}

		CryLogAlways("<Sound> Playback drivers found: %d\n", nTemp);
		CryFixedStringT<1024> sDriversString("<Sound> Available playback drivers:");

		for (int i = 0; i < nTemp; ++i)
		{
			char sDriverName[MAXCHARBUFFERSIZE] = {'\0'};
			m_ExResult = m_pAPISystem->getDriverInfo(i, sDriverName, MAXCHARBUFFERSIZE, 0);

			if (!IS_FMODERROR)
			{
				char cTemp[4] = {'\0'}; // There won't be more than 999 playback drivers. :)
				sprintf_s(cTemp, 4, "%d", i+1);
				sDriversString +=  "\n\t";
				sDriversString +=  cTemp;
				sDriversString +=  ".\t";
				sDriversString +=  sDriverName;
			}
		}

		CryLogAlways(sDriversString.c_str());

		if (m_pSoundSystem->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
		{
			m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::SYSTEM_SETDRIVER, (int)gEnv->pTimer->GetFrameStartTime().GetMilliSeconds());
		}

		if (bSetDriver)
		{
			m_ExResult = m_pAPISystem->setDriver(0);
		}
		else
		{
			// It's ok to query the current driver though.
			m_ExResult = FMOD_OK;
		}

		if (!IS_FMODERROR)
		{
			// Querying exactly which driver got set.
			m_ExResult = m_pAPISystem->getDriver(&nTemp);

			if (!IS_FMODERROR)
			{
				char sDriverName[MAXCHARBUFFERSIZE] = {'\0'};
				m_ExResult = m_pAPISystem->getDriverInfo(nTemp, sDriverName, MAXCHARBUFFERSIZE, 0);

				if (!IS_FMODERROR)
				{
					CryLogAlways("<Sound> FmodEx-AudioDevice: Now running on driver: %s\n", sDriverName);
				}
				else
				{
					FmodErrorOutput("get driver name failed!", eSLT_Error);
				}
			}
			else
			{
				FmodErrorOutput("get driver failed!", eSLT_Error);
			}
		}
		else
		{
			FmodErrorOutput("set driver to default failed!", eSLT_Error);
		}

		if (m_ExResult == FMOD_OK)
		{
			// Now query for available record driver.




			m_ExResult = m_pAPISystem->getRecordNumDrivers(&nTemp);


			if (nTemp > 0)
			{
				CryLogAlways("<Sound> Record drivers found: %d\n", nTemp);
				sDriversString = "<Sound> Available record drivers:";

				for (int i = 0; i < nTemp; ++i)
				{
					char sDriverName[MAXCHARBUFFERSIZE] = {'\0'};
					m_ExResult = m_pAPISystem->getRecordDriverInfo(i, sDriverName, MAXCHARBUFFERSIZE, 0);

					if (!IS_FMODERROR)
					{
						char cTemp[4] = {'\0'}; // There won't be more than 999 record drivers. :)
						sprintf_s(cTemp, 4, "%d", i+1);
						sDriversString +=  "\n\t";
						sDriversString +=  cTemp;
						sDriversString +=  ".\t";
						sDriversString +=  sDriverName;
					}
				}

				CryLogAlways(sDriversString.c_str());
			}
			else
			{
				CryLogAlways("<Sound> No record drivers found!\n");
			}

			if (m_pSoundSystem->g_nRecordConfig == 0)
			{
				CryLogAlways("<Sound> Record config is set to autodetect using record driver 0\n");
			}
			else
			{
				CryLogAlways("<Sound> Record config is set to explicitly use record driver %d\n", m_pSoundSystem->g_nRecordConfig-1); // decrease because FMOD index starts at 0
			}

			// set Record Driver
			if (m_pSoundSystem->g_nDebugSound == SOUNDSYSTEM_DEBUG_RECORD_COMMANDS)
			{
				m_CommandPlayer.LogCommand(CCommandPlayerFmodEx400::SYSTEM_SETRECORDDRIVER, (int)gEnv->pTimer->GetFrameStartTime().GetMilliSeconds(), ptParamUINT32(m_pSoundSystem->g_nRecordConfig-1));
			}
		}

		return m_ExResult == FMOD_OK;
	}

#endif
